Merge changes I2e8a8f64,I72769fab into main

* changes:
  crosvm: gunyah: Support launching QTVMs
  hypervisor: gunyah: Add balloon support
diff --git a/hypervisor/src/gunyah/aarch64.rs b/hypervisor/src/gunyah/aarch64.rs
index b1f3e70..3d08c53 100644
--- a/hypervisor/src/gunyah/aarch64.rs
+++ b/hypervisor/src/gunyah/aarch64.rs
@@ -130,21 +130,13 @@
             bell_node.set_prop("interrupts", &interrupts)?;
         }
 
-        let mut base_set = false;
         for region in self.guest_mem.regions() {
             let create_shm_node = match region.options.purpose {
                 MemoryRegionPurpose::Bios => false,
-                MemoryRegionPurpose::GuestMemoryRegion => {
-                    // Assume first GuestMemoryRegion contains the payload
-                    // This memory region is described by the "base-address" property
-                    // and doesn't get re-described as a separate shm node.
-                    let ret = base_set;
-                    base_set = true;
-                    ret
-                }
+                MemoryRegionPurpose::GuestMemoryRegion => false,
                 // Described by the "firmware-address" property
                 MemoryRegionPurpose::ProtectedFirmwareRegion => false,
-                MemoryRegionPurpose::ReservedMemory => true,
+                MemoryRegionPurpose::ReservedMemory => false,
                 MemoryRegionPurpose::StaticSwiotlbRegion => true,
             };
 
@@ -166,7 +158,6 @@
         fdt_address: GuestAddress,
         fdt_size: usize,
     ) -> Result<()> {
-        // Gunyah sets the PC to the payload entry point, except for protected VMs.
         // The payload entry is the memory address where the kernel starts.
         // This memory region contains both the DTB and the kernel image,
         // so ensure they are located together.
@@ -184,8 +175,32 @@
             panic!("DTB and payload are not part of same memory region.");
         }
 
+        if self.vm_id.is_some() && self.pas_id.is_some() {
+            // Gunyah will find the metadata about the Qualcomm Trusted VM in the
+            // first few pages (decided at build time) of the primary payload region.
+            // This metadata consists of the elf header which tells Gunyah where
+            // the different elf segments (kernel/DTB/ramdisk) are. As we send the entire
+            // primary payload as a single memory parcel to Gunyah, with the offsets from
+            // the elf header, Gunyah can find the VM DTBOs.
+            // Pass on the primary payload region start address and its size for Qualcomm
+            // Trusted VMs.
+            for region in self.guest_mem.regions() {
+                if region.guest_addr.offset() == payload_entry_address.offset() {
+                    self.set_vm_auth_type_to_qcom_trusted_vm(payload_entry_address, region.size.try_into().unwrap());
+                    break;
+                }
+            }
+        }
+
         self.set_dtb_config(fdt_address, fdt_size)?;
 
+        // Gunyah sets the PC to the payload entry point for protected VMs without firmware.
+        // It needs to be 0 as Gunyah assumes it to be kernel start.
+        if self.hv_cfg.protection_type.isolates_memory() &&
+           !self.hv_cfg.protection_type.runs_firmware() && payload_offset != 0 {
+            panic!("Payload offset must be zero");
+        }
+
         if let Err(e) = self.set_boot_pc(payload_entry_address.offset()) {
             if e.errno() == ENOTTY {
                 // GH_VM_SET_BOOT_CONTEXT ioctl is not supported, but returning success
diff --git a/hypervisor/src/gunyah/gunyah_sys.rs b/hypervisor/src/gunyah/gunyah_sys.rs
index 1097218..c52e567 100644
--- a/hypervisor/src/gunyah/gunyah_sys.rs
+++ b/hypervisor/src/gunyah/gunyah_sys.rs
@@ -53,3 +53,21 @@
     0x12,
     gh_vm_firmware_config
 );
+ioctl_iow_nr!(
+    GH_VM_RECLAIM_REGION,
+    GH_ANDROID_IOCTL_TYPE,
+    0x13,
+    gunyah_address_range
+);
+ioctl_iow_nr!(
+    GH_VM_ANDROID_MAP_CMA_MEM,
+    GH_ANDROID_IOCTL_TYPE,
+    0x15,
+    gunyah_map_cma_mem_args
+);
+ioctl_iow_nr!(
+    GH_VM_ANDROID_SET_AUTH_TYPE,
+    GH_ANDROID_IOCTL_TYPE,
+    0x16,
+    gunyah_auth_desc
+);
diff --git a/hypervisor/src/gunyah/gunyah_sys/bindings.rs b/hypervisor/src/gunyah/gunyah_sys/bindings.rs
index 325c431..19e378d 100644
--- a/hypervisor/src/gunyah/gunyah_sys/bindings.rs
+++ b/hypervisor/src/gunyah/gunyah_sys/bindings.rs
@@ -24,6 +24,16 @@
 pub const GH_VCPU_EXIT_STATUS: u32 = 2;
 pub const GH_ANDROID_IOCTL_TYPE: u8 = 65u8;
 pub const GH_VM_BOOT_CONTEXT_REG_SHIFT: u32 = 8;
+pub type i8 = :: std :: os :: raw :: c_schar;
+pub type u8 = :: std :: os :: raw :: c_uchar;
+pub type i16 = :: std :: os :: raw :: c_short;
+pub type u16 = :: std :: os :: raw :: c_ushort;
+pub type i32 = :: std :: os :: raw :: c_int;
+pub type u32 = :: std :: os :: raw :: c_uint;
+pub type i64 = :: std :: os :: raw :: c_longlong;
+pub type u64 = :: std :: os :: raw :: c_ulonglong;
+pub type __s128 = i128;
+pub type __u128 = u128;
 pub type __le16 = u16;
 pub type __be16 = u16;
 pub type __le32 = u32;
@@ -32,7 +42,25 @@
 pub type __be64 = u64;
 pub type __sum16 = u16;
 pub type __wsum = u32;
-pub type __poll_t = ::std::os::raw::c_uint;
+pub type __poll_t = :: std :: os :: raw :: c_uint;
+pub const gunyah_auth_type_GUNYAH_ANDROID_PVM_TYPE : gunyah_auth_type = 0;
+pub const gunyah_auth_type_GUNYAH_QCOM_TRUSTED_VM_TYPE : gunyah_auth_type = 1;
+pub type gunyah_auth_type = :: std :: os :: raw :: c_uint;
+#[repr (C)]
+#[derive (Debug , Default , Copy , Clone)]
+pub struct gunyah_qtvm_auth_arg {
+    pub vm_id : u16,
+    pub pas_id : u32,
+    pub guest_phys_addr : u64,
+    pub size : u64,
+}
+#[repr (C)]
+#[derive (Debug , Default , Copy , Clone)]
+pub struct gunyah_auth_desc {
+    pub type_ : u32,
+    pub arg_size : u32,
+    pub arg : u64,
+}
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct gh_userspace_memory_region {
@@ -164,3 +192,28 @@
     pub guest_phys_addr: u64,
     pub size: u64,
 }
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct gunyah_address_range {
+    pub guest_phys_addr: u64,
+    pub size: u64,
+}
+pub const GUNYAH_MEM_ALLOW_READ : gunyah_map_flags = 1 ;
+pub const GUNYAH_MEM_ALLOW_WRITE : gunyah_map_flags = 2 ;
+pub const GUNYAH_MEM_ALLOW_EXEC : gunyah_map_flags = 4 ;
+pub const GUNYAH_MEM_ALLOW_RWX : gunyah_map_flags = 7 ;
+pub const GUNYAH_MEM_DEFAULT_ACCESS : gunyah_map_flags = 0 ;
+pub const GUNYAH_MEM_FORCE_LEND : gunyah_map_flags = 16 ;
+pub const GUNYAH_MEM_FORCE_SHARE : gunyah_map_flags = 32 ;
+pub const GUNYAH_MEM_UNMAP : gunyah_map_flags = 256 ;
+pub type gunyah_map_flags = :: std :: os :: raw :: c_uint ;
+#[repr (C)]
+#[derive (Debug , Default , Copy , Clone)]
+pub struct gunyah_map_cma_mem_args {
+    pub label: u32,
+    pub guest_addr : u64,
+    pub flags : u32,
+    pub guest_mem_fd : u32,
+    pub offset : u64,
+    pub size : u64,
+}
diff --git a/hypervisor/src/gunyah/mod.rs b/hypervisor/src/gunyah/mod.rs
index 472dd77..2ab6c39 100644
--- a/hypervisor/src/gunyah/mod.rs
+++ b/hypervisor/src/gunyah/mod.rs
@@ -19,6 +19,7 @@
 use std::path::PathBuf;
 use std::sync::Arc;
 
+use anyhow::Context;
 use base::errno_result;
 use base::error;
 use base::info;
@@ -171,6 +172,44 @@
     }
 }
 
+fn map_cma_region(
+    vm: &SafeDescriptor,
+    slot: MemSlot,
+    lend: bool,
+    read_only: bool,
+    guest_addr: u64,
+    guest_mem_fd: u32,
+    size: u64,
+    offset: u64,
+) -> Result<()> {
+    let mut flags = 0;
+    flags |= GUNYAH_MEM_ALLOW_READ | GUNYAH_MEM_ALLOW_EXEC;
+    if !read_only {
+        flags |= GUNYAH_MEM_ALLOW_WRITE;
+    }
+    if lend {
+        flags |= GUNYAH_MEM_FORCE_LEND;
+    }
+    else {
+        flags |= GUNYAH_MEM_FORCE_SHARE;
+    }
+    let region = gunyah_map_cma_mem_args {
+        label: slot,
+        guest_addr,
+        flags,
+        guest_mem_fd,
+        offset,
+        size,
+    };
+    // SAFETY: safe because the return value is checked.
+    let ret = unsafe { ioctl_with_ref(vm, GH_VM_ANDROID_MAP_CMA_MEM, &region) };
+    if ret == 0 {
+        Ok(())
+    } else {
+        errno_result()
+    }
+}
+
 #[derive(PartialEq, Eq, Hash)]
 pub struct GunyahIrqRoute {
     irq: u32,
@@ -180,6 +219,8 @@
 pub struct GunyahVm {
     gh: Gunyah,
     vm: SafeDescriptor,
+    vm_id: Option<u16>,
+    pas_id: Option<u32>,
     guest_mem: GuestMemory,
     mem_regions: Arc<Mutex<BTreeMap<MemSlot, (Box<dyn MappedRegion>, GuestAddress)>>>,
     /// A min heap of MemSlot numbers that were used and then removed and can now be re-used
@@ -195,7 +236,7 @@
 }
 
 impl GunyahVm {
-    pub fn new(gh: &Gunyah, guest_mem: GuestMemory, cfg: Config) -> Result<GunyahVm> {
+    pub fn new(gh: &Gunyah, vm_id: Option<u16>, pas_id: Option<u32>, guest_mem: GuestMemory, cfg: Config) -> Result<GunyahVm> {
         // SAFETY:
         // Safe because we know gunyah is a real gunyah fd as this module is the only one that can
         // make Gunyah objects.
@@ -221,7 +262,18 @@
             } else {
                 false
             };
-            if lend {
+            if region.options.file_backed.is_some() {
+                map_cma_region(
+                        &vm_descriptor,
+                        region.index as MemSlot,
+                        lend,
+                        !region.options.file_backed.unwrap().writable,
+                        region.guest_addr.offset(),
+                        region.shm.as_raw_descriptor().try_into().unwrap(),
+                        region.size.try_into().unwrap(),
+                        region.shm_offset,
+                )?;
+            } else if lend {
                 // SAFETY:
                 // Safe because the guest regions are guarnteed not to overlap.
                 unsafe {
@@ -253,6 +305,8 @@
         Ok(GunyahVm {
             gh: gh.try_clone()?,
             vm: vm_descriptor,
+            vm_id,
+            pas_id,
             guest_mem,
             mem_regions: Arc::new(Mutex::new(BTreeMap::new())),
             mem_slot_gaps: Arc::new(Mutex::new(BinaryHeap::new())),
@@ -261,6 +315,28 @@
         })
     }
 
+    pub fn set_vm_auth_type_to_qcom_trusted_vm(&self, payload_start: GuestAddress, payload_size: u64) -> Result<()> {
+        let gunyah_qtvm_auth_arg = gunyah_qtvm_auth_arg {
+            vm_id: self.vm_id.expect("VM ID not specified for a QTVM"),
+            pas_id: self.pas_id.expect("PAS ID not specified for a QTVM"),
+            // QTVMs have the metadata needed for authentication at the start of the guest addrspace.
+            guest_phys_addr: payload_start.offset(),
+            size: payload_size,
+        };
+        let gunyah_auth_desc = gunyah_auth_desc {
+            type_: gunyah_auth_type_GUNYAH_QCOM_TRUSTED_VM_TYPE,
+            arg_size: size_of::<gunyah_qtvm_auth_arg>() as u32,
+            arg: &gunyah_qtvm_auth_arg as *const gunyah_qtvm_auth_arg as u64,
+        };
+        // SAFETY: safe because the return value is checked.
+        let ret = unsafe { ioctl_with_ref(self, GH_VM_ANDROID_SET_AUTH_TYPE, &gunyah_auth_desc) };
+        if ret == 0 {
+            Ok(())
+        } else {
+            errno_result()
+        }
+    }
+
     fn create_vcpu(&self, id: usize) -> Result<GunyahVcpu> {
         let gh_fn_vcpu_arg = gh_fn_vcpu_arg {
             id: id.try_into().unwrap(),
@@ -363,6 +439,8 @@
         Ok(GunyahVm {
             gh: self.gh.try_clone()?,
             vm: self.vm.try_clone()?,
+            vm_id: self.vm_id,
+            pas_id: self.pas_id,
             guest_mem: self.guest_mem.clone(),
             mem_regions: self.mem_regions.clone(),
             mem_slot_gaps: self.mem_slot_gaps.clone(),
@@ -440,6 +518,26 @@
             errno_result()
         }
     }
+
+    fn handle_inflate(&self, guest_addr: GuestAddress, size: u64) -> Result<()> {
+        let range = gunyah_address_range {
+            guest_phys_addr: guest_addr.0,
+            size,
+        };
+
+        // SAFETY: Safe because we know this is a Gunyah VM
+        let ret = unsafe { ioctl_with_ref(self, GH_VM_RECLAIM_REGION, &range) };
+        if ret != 0 {
+            warn!("Gunyah failed to reclaim {:?}", range);
+            return errno_result();
+        }
+
+        match self.guest_mem.remove_range(guest_addr, size) {
+            Ok(_) => Ok(()),
+            Err(vm_memory::Error::MemoryAccess(_, MmapError::SystemCallFailed(e))) => Err(e),
+            Err(_) => Err(Error::new(EIO)),
+        }
+    }
 }
 
 impl Vm for GunyahVm {
@@ -450,6 +548,8 @@
         Ok(GunyahVm {
             gh: self.gh.try_clone()?,
             vm: self.vm.try_clone()?,
+            vm_id: self.vm_id,
+            pas_id: self.pas_id,
             guest_mem: self.guest_mem.clone(),
             mem_regions: self.mem_regions.clone(),
             mem_slot_gaps: self.mem_slot_gaps.clone(),
@@ -715,8 +815,12 @@
         }
     }
 
-    fn handle_balloon_event(&mut self, _event: BalloonEvent) -> Result<()> {
-        unimplemented!()
+    fn handle_balloon_event(&mut self, event: BalloonEvent) -> Result<()> {
+        match event {
+            BalloonEvent::Inflate(m) => self.handle_inflate(m.guest_address, m.size),
+            BalloonEvent::Deflate(m) => Ok(()),
+            BalloonEvent::BalloonTargetReached(_) => Ok(()),
+        }
     }
 }
 
diff --git a/src/crosvm/sys/linux.rs b/src/crosvm/sys/linux.rs
index 737a6a6..50e3b05 100644
--- a/src/crosvm/sys/linux.rs
+++ b/src/crosvm/sys/linux.rs
@@ -1859,6 +1859,8 @@
 #[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), feature = "gunyah"))]
 fn run_gunyah(
     device_path: Option<&Path>,
+    qcom_trusted_vm_id: Option<u16>,
+    qcom_trusted_vm_pas_id: Option<u32>,
     cfg: Config,
     components: VmComponents,
 ) -> Result<ExitState> {
@@ -1885,7 +1887,7 @@
         None
     };
 
-    let vm = GunyahVm::new(&gunyah, guest_mem, components.hv_cfg).context("failed to create vm")?;
+    let vm = GunyahVm::new(&gunyah, qcom_trusted_vm_id, qcom_trusted_vm_pas_id, guest_mem, components.hv_cfg).context("failed to create vm")?;
 
     // Check that the VM was actually created in protected mode as expected.
     if cfg.protection_type.isolates_memory() && !vm.check_capability(VmCap::Protected) {
@@ -1936,6 +1938,8 @@
         if gunyah_path.exists() {
             return Some(HypervisorKind::Gunyah {
                 device: Some(gunyah_path.to_path_buf()),
+                qcom_trusted_vm_id: None,
+                qcom_trusted_vm_pas_id: None,
             });
         }
     }
@@ -1964,7 +1968,14 @@
             any(target_arch = "arm", target_arch = "aarch64"),
             feature = "gunyah"
         ))]
-        HypervisorKind::Gunyah { device } => run_gunyah(device.as_deref(), cfg, components),
+        HypervisorKind::Gunyah { device,
+                                 qcom_trusted_vm_id,
+                                 qcom_trusted_vm_pas_id
+                               } => run_gunyah(
+                                        device.as_deref(),
+                                        qcom_trusted_vm_id,
+                                        qcom_trusted_vm_pas_id,
+                                        cfg, components),
     }
 }
 
diff --git a/src/crosvm/sys/linux/config.rs b/src/crosvm/sys/linux/config.rs
index 597156d..172a554 100644
--- a/src/crosvm/sys/linux/config.rs
+++ b/src/crosvm/sys/linux/config.rs
@@ -34,6 +34,8 @@
     #[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), feature = "gunyah"))]
     Gunyah {
         device: Option<PathBuf>,
+        qcom_trusted_vm_id: Option<u16>,
+        qcom_trusted_vm_pas_id: Option<u32>,
     },
 }
 
@@ -617,7 +619,11 @@
 
         assert_eq!(
             config.hypervisor,
-            Some(HypervisorKind::Gunyah { device: None })
+            Some(HypervisorKind::Gunyah {
+                device: None,
+                qcom_trusted_vm_id: None,
+                qcom_trusted_vm_pas_id: None,
+            })
         );
     }
 
@@ -635,7 +641,30 @@
         assert_eq!(
             config.hypervisor,
             Some(HypervisorKind::Gunyah {
-                device: Some(PathBuf::from("/not/default"))
+                device: Some(PathBuf::from("/not/default")),
+                qcom_trusted_vm_id: None,
+                qcom_trusted_vm_pas_id: None,
+            })
+        );
+    }
+
+    #[test]
+    #[cfg(all(any(target_arch = "arm", target_arch = "aarch64"), feature = "gunyah"))]
+    fn hypervisor_gunyah_device_with_qtvm_ids() {
+        let config: Config = crate::crosvm::cmdline::RunCommand::from_args(
+            &[],
+            &["--hypervisor", "gunyah[device=/not/default,qcom_trusted_vm_id=0,qcom_trusted_vm_pas_id=0]", "/dev/null"],
+        )
+        .unwrap()
+        .try_into()
+        .unwrap();
+
+        assert_eq!(
+            config.hypervisor,
+            Some(HypervisorKind::Gunyah {
+                device: Some(PathBuf::from("/not/default")),
+                qcom_trusted_vm_id: Some(0),
+                qcom_trusted_vm_pas_id: Some(0),
             })
         );
     }