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 range. dynamically_free_memory_range( &self, guest_address: GuestAddress, size: u64, ) -> Result<()>130 pub fn dynamically_free_memory_range( 131 &self, 132 guest_address: GuestAddress, 133 size: u64, 134 ) -> Result<()> { 135 self.request_unit(&VmMemoryRequest::DynamicallyFreeMemoryRange { 136 guest_address, 137 size, 138 }) 139 } 140 141 /// Call hypervisor to reclaim a priorly freed memory range. dynamically_reclaim_memory_range( &self, guest_address: GuestAddress, size: u64, ) -> Result<()>142 pub fn dynamically_reclaim_memory_range( 143 &self, 144 guest_address: GuestAddress, 145 size: u64, 146 ) -> Result<()> { 147 self.request_unit(&VmMemoryRequest::DynamicallyReclaimMemoryRange { 148 guest_address, 149 size, 150 }) 151 } 152 153 /// Unregister the given memory slot that was previously registered with `RegisterMemory`. unregister_memory(&self, region: VmMemoryRegionId) -> Result<()>154 pub fn unregister_memory(&self, region: VmMemoryRegionId) -> Result<()> { 155 self.request_unit(&VmMemoryRequest::UnregisterMemory(region)) 156 } 157 158 /// Register an ioeventfd by looking up using Alloc info. register_io_event_with_alloc( &self, evt: Event, allocation: Alloc, offset: u64, datamatch: Datamatch, ) -> Result<()>159 pub fn register_io_event_with_alloc( 160 &self, 161 evt: Event, 162 allocation: Alloc, 163 offset: u64, 164 datamatch: Datamatch, 165 ) -> Result<()> { 166 self.request_unit(&VmMemoryRequest::IoEventWithAlloc { 167 evt, 168 allocation, 169 offset, 170 datamatch, 171 register: true, 172 }) 173 } 174 175 /// Unregister an eventfd by looking up using Alloc info. unregister_io_event_with_alloc( &self, evt: Event, allocation: Alloc, offset: u64, datamatch: Datamatch, ) -> Result<()>176 pub fn unregister_io_event_with_alloc( 177 &self, 178 evt: Event, 179 allocation: Alloc, 180 offset: u64, 181 datamatch: Datamatch, 182 ) -> Result<()> { 183 self.request_unit(&VmMemoryRequest::IoEventWithAlloc { 184 evt, 185 allocation, 186 offset, 187 datamatch, 188 register: false, 189 }) 190 } 191 192 /// Register an eventfd with raw guest memory address. register_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()>193 pub fn register_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()> { 194 self.request_unit(&VmMemoryRequest::IoEventRaw(IoEventUpdateRequest { 195 event, 196 addr, 197 datamatch, 198 register: true, 199 })) 200 } 201 202 /// Unregister an eventfd with raw guest memory address. unregister_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()>203 pub fn unregister_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()> { 204 self.request_unit(&VmMemoryRequest::IoEventRaw(IoEventUpdateRequest { 205 event, 206 addr, 207 datamatch, 208 register: false, 209 })) 210 } 211 balloon_target_reached(&self, size: u64) -> Result<()>212 pub fn balloon_target_reached(&self, size: u64) -> Result<()> { 213 self.request_unit(&VmMemoryRequest::BalloonTargetReached { size }) 214 } 215 } 216 217 impl AsRawDescriptor for VmMemoryClient { as_raw_descriptor(&self) -> RawDescriptor218 fn as_raw_descriptor(&self) -> RawDescriptor { 219 self.tube.as_raw_descriptor() 220 } 221 } 222