1 // Copyright 2023 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //! vm_control API client for use within crosvm 6 7 use base::AsRawDescriptor; 8 use base::Event; 9 use base::Protection; 10 use base::RawDescriptor; 11 use base::Tube; 12 use base::TubeError; 13 use hypervisor::Datamatch; 14 use hypervisor::MemCacheType; 15 use remain::sorted; 16 use resources::Alloc; 17 use serde::Deserialize; 18 use serde::Serialize; 19 use thiserror::Error; 20 use vm_memory::GuestAddress; 21 22 use crate::IoEventUpdateRequest; 23 use crate::VmMemoryDestination; 24 use crate::VmMemoryRegionId; 25 use crate::VmMemoryRequest; 26 use crate::VmMemoryResponse; 27 use crate::VmMemorySource; 28 29 #[derive(Error, Debug)] 30 #[sorted] 31 pub enum ApiClientError { 32 #[error("API client tube recv failed: {0}")] 33 Recv(TubeError), 34 #[error("Request failed: {0}")] 35 RequestFailed(#[from] base::Error), 36 #[error("API client tube send failed: {0}")] 37 Send(TubeError), 38 #[error("API client tube sending FDs failed: {0}")] 39 SendFds(TubeError), 40 #[error("Unexpected tube response")] 41 UnexpectedResponse, 42 } 43 44 pub type Result<T> = std::result::Result<T, ApiClientError>; 45 46 #[derive(Serialize, Deserialize)] 47 pub struct VmMemoryClient { 48 tube: Tube, 49 } 50 51 impl VmMemoryClient { new(tube: Tube) -> Self52 pub fn new(tube: Tube) -> Self { 53 VmMemoryClient { tube } 54 } 55 request(&self, request: &VmMemoryRequest) -> Result<VmMemoryResponse>56 fn request(&self, request: &VmMemoryRequest) -> Result<VmMemoryResponse> { 57 self.tube.send(request).map_err(ApiClientError::Send)?; 58 self.tube 59 .recv::<VmMemoryResponse>() 60 .map_err(ApiClientError::Recv) 61 } 62 request_unit(&self, request: &VmMemoryRequest) -> Result<()>63 fn request_unit(&self, request: &VmMemoryRequest) -> Result<()> { 64 match self.request(request)? { 65 VmMemoryResponse::Ok => Ok(()), 66 VmMemoryResponse::Err(e) => Err(ApiClientError::RequestFailed(e)), 67 _other => Err(ApiClientError::UnexpectedResponse), 68 } 69 } 70 71 /// Prepare a shared memory region to make later operations more efficient. This 72 /// may be a no-op depending on underlying platform support. prepare_shared_memory_region(&self, alloc: Alloc, cache: MemCacheType) -> Result<()>73 pub fn prepare_shared_memory_region(&self, alloc: Alloc, cache: MemCacheType) -> Result<()> { 74 self.request_unit(&VmMemoryRequest::PrepareSharedMemoryRegion { alloc, cache }) 75 } 76 register_memory( &self, source: VmMemorySource, dest: VmMemoryDestination, prot: Protection, cache: MemCacheType, ) -> Result<VmMemoryRegionId>77 pub fn register_memory( 78 &self, 79 source: VmMemorySource, 80 dest: VmMemoryDestination, 81 prot: Protection, 82 cache: MemCacheType, 83 ) -> Result<VmMemoryRegionId> { 84 let request = VmMemoryRequest::RegisterMemory { 85 source, 86 dest, 87 prot, 88 cache, 89 }; 90 match self.request(&request)? { 91 VmMemoryResponse::Err(e) => Err(ApiClientError::RequestFailed(e)), 92 VmMemoryResponse::RegisterMemory { region_id, .. } => Ok(region_id), 93 _other => Err(ApiClientError::UnexpectedResponse), 94 } 95 } 96 97 #[cfg(any(target_os = "android", target_os = "linux"))] mmap_and_register_memory( &self, mapping_address: GuestAddress, shm: base::SharedMemory, file_mapping_info: Vec<crate::VmMemoryFileMapping>, ) -> Result<u32>98 pub fn mmap_and_register_memory( 99 &self, 100 mapping_address: GuestAddress, 101 shm: base::SharedMemory, 102 file_mapping_info: Vec<crate::VmMemoryFileMapping>, 103 ) -> Result<u32> { 104 let num_file_mappings = file_mapping_info.len(); 105 let req = VmMemoryRequest::MmapAndRegisterMemory { 106 shm, 107 dest: VmMemoryDestination::GuestPhysicalAddress(mapping_address.0), 108 num_file_mappings, 109 }; 110 111 self.tube.send(&req).map_err(ApiClientError::Send)?; 112 113 // Since the number of FDs that can be sent via Tube at once is limited to 114 // SCM_MAX_FD, split `file_mappings` to chunks and send them 115 // repeatedly. 116 for m in file_mapping_info.chunks(base::unix::SCM_MAX_FD) { 117 self.tube 118 .send_with_max_fds(&m, m.len()) 119 .map_err(ApiClientError::SendFds)?; 120 } 121 122 match self.tube.recv().map_err(ApiClientError::Recv)? { 123 VmMemoryResponse::RegisterMemory { slot, .. } => Ok(slot), 124 VmMemoryResponse::Err(e) => Err(ApiClientError::RequestFailed(e)), 125 _ => Err(ApiClientError::UnexpectedResponse), 126 } 127 } 128 129 /// Call hypervisor to free the given memory ranges. dynamically_free_memory_ranges(&self, ranges: Vec<(GuestAddress, u64)>) -> Result<()>130 pub fn dynamically_free_memory_ranges(&self, ranges: Vec<(GuestAddress, u64)>) -> Result<()> { 131 self.request_unit(&VmMemoryRequest::DynamicallyFreeMemoryRanges { ranges }) 132 } 133 134 /// Call hypervisor to reclaim a priorly freed memory range. dynamically_reclaim_memory_ranges( &self, ranges: Vec<(GuestAddress, u64)>, ) -> Result<()>135 pub fn dynamically_reclaim_memory_ranges( 136 &self, 137 ranges: Vec<(GuestAddress, u64)>, 138 ) -> Result<()> { 139 self.request_unit(&VmMemoryRequest::DynamicallyReclaimMemoryRanges { ranges }) 140 } 141 142 /// Unregister the given memory slot that was previously registered with `RegisterMemory`. unregister_memory(&self, region: VmMemoryRegionId) -> Result<()>143 pub fn unregister_memory(&self, region: VmMemoryRegionId) -> Result<()> { 144 self.request_unit(&VmMemoryRequest::UnregisterMemory(region)) 145 } 146 147 /// Register an eventfd with raw guest memory address. register_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()>148 pub fn register_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()> { 149 self.request_unit(&VmMemoryRequest::IoEventRaw(IoEventUpdateRequest { 150 event, 151 addr, 152 datamatch, 153 register: true, 154 })) 155 } 156 157 /// Unregister an eventfd with raw guest memory address. unregister_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()>158 pub fn unregister_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()> { 159 self.request_unit(&VmMemoryRequest::IoEventRaw(IoEventUpdateRequest { 160 event, 161 addr, 162 datamatch, 163 register: false, 164 })) 165 } 166 balloon_target_reached(&self, size: u64) -> Result<()>167 pub fn balloon_target_reached(&self, size: u64) -> Result<()> { 168 self.request_unit(&VmMemoryRequest::BalloonTargetReached { size }) 169 } 170 } 171 172 impl AsRawDescriptor for VmMemoryClient { as_raw_descriptor(&self) -> RawDescriptor173 fn as_raw_descriptor(&self) -> RawDescriptor { 174 self.tube.as_raw_descriptor() 175 } 176 } 177