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("Unexpected tube response")] 39 UnexpectedResponse, 40 } 41 42 pub type Result<T> = std::result::Result<T, ApiClientError>; 43 44 #[derive(Serialize, Deserialize)] 45 pub struct VmMemoryClient { 46 tube: Tube, 47 } 48 49 impl VmMemoryClient { new(tube: Tube) -> Self50 pub fn new(tube: Tube) -> Self { 51 VmMemoryClient { tube } 52 } 53 request(&self, request: &VmMemoryRequest) -> Result<VmMemoryResponse>54 fn request(&self, request: &VmMemoryRequest) -> Result<VmMemoryResponse> { 55 self.tube.send(request).map_err(ApiClientError::Send)?; 56 self.tube 57 .recv::<VmMemoryResponse>() 58 .map_err(ApiClientError::Recv) 59 } 60 request_unit(&self, request: &VmMemoryRequest) -> Result<()>61 fn request_unit(&self, request: &VmMemoryRequest) -> Result<()> { 62 match self.request(request)? { 63 VmMemoryResponse::Ok => Ok(()), 64 VmMemoryResponse::Err(e) => Err(ApiClientError::RequestFailed(e)), 65 _other => Err(ApiClientError::UnexpectedResponse), 66 } 67 } 68 69 /// Prepare a shared memory region to make later operations more efficient. This 70 /// may be a no-op depending on underlying platform support. prepare_shared_memory_region(&self, alloc: Alloc, cache: MemCacheType) -> Result<()>71 pub fn prepare_shared_memory_region(&self, alloc: Alloc, cache: MemCacheType) -> Result<()> { 72 self.request_unit(&VmMemoryRequest::PrepareSharedMemoryRegion { alloc, cache }) 73 } 74 register_memory( &self, source: VmMemorySource, dest: VmMemoryDestination, prot: Protection, cache: MemCacheType, ) -> Result<VmMemoryRegionId>75 pub fn register_memory( 76 &self, 77 source: VmMemorySource, 78 dest: VmMemoryDestination, 79 prot: Protection, 80 cache: MemCacheType, 81 ) -> Result<VmMemoryRegionId> { 82 let request = VmMemoryRequest::RegisterMemory { 83 source, 84 dest, 85 prot, 86 cache, 87 }; 88 match self.request(&request)? { 89 VmMemoryResponse::Err(e) => Err(ApiClientError::RequestFailed(e)), 90 VmMemoryResponse::RegisterMemory(region_id) => Ok(region_id), 91 _other => Err(ApiClientError::UnexpectedResponse), 92 } 93 } 94 95 /// Call hypervisor to free the given memory range. dynamically_free_memory_range( &self, guest_address: GuestAddress, size: u64, ) -> Result<()>96 pub fn dynamically_free_memory_range( 97 &self, 98 guest_address: GuestAddress, 99 size: u64, 100 ) -> Result<()> { 101 self.request_unit(&VmMemoryRequest::DynamicallyFreeMemoryRange { 102 guest_address, 103 size, 104 }) 105 } 106 107 /// Call hypervisor to reclaim a priorly freed memory range. dynamically_reclaim_memory_range( &self, guest_address: GuestAddress, size: u64, ) -> Result<()>108 pub fn dynamically_reclaim_memory_range( 109 &self, 110 guest_address: GuestAddress, 111 size: u64, 112 ) -> Result<()> { 113 self.request_unit(&VmMemoryRequest::DynamicallyReclaimMemoryRange { 114 guest_address, 115 size, 116 }) 117 } 118 119 /// Unregister the given memory slot that was previously registered with `RegisterMemory`. unregister_memory(&self, region: VmMemoryRegionId) -> Result<()>120 pub fn unregister_memory(&self, region: VmMemoryRegionId) -> Result<()> { 121 self.request_unit(&VmMemoryRequest::UnregisterMemory(region)) 122 } 123 124 /// Register an ioeventfd by looking up using Alloc info. register_io_event_with_alloc( &self, evt: Event, allocation: Alloc, offset: u64, datamatch: Datamatch, ) -> Result<()>125 pub fn register_io_event_with_alloc( 126 &self, 127 evt: Event, 128 allocation: Alloc, 129 offset: u64, 130 datamatch: Datamatch, 131 ) -> Result<()> { 132 self.request_unit(&VmMemoryRequest::IoEventWithAlloc { 133 evt, 134 allocation, 135 offset, 136 datamatch, 137 register: true, 138 }) 139 } 140 141 /// Unregister an eventfd by looking up using Alloc info. unregister_io_event_with_alloc( &self, evt: Event, allocation: Alloc, offset: u64, datamatch: Datamatch, ) -> Result<()>142 pub fn unregister_io_event_with_alloc( 143 &self, 144 evt: Event, 145 allocation: Alloc, 146 offset: u64, 147 datamatch: Datamatch, 148 ) -> Result<()> { 149 self.request_unit(&VmMemoryRequest::IoEventWithAlloc { 150 evt, 151 allocation, 152 offset, 153 datamatch, 154 register: false, 155 }) 156 } 157 158 /// Register an eventfd with raw guest memory address. register_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()>159 pub fn register_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()> { 160 self.request_unit(&VmMemoryRequest::IoEventRaw(IoEventUpdateRequest { 161 event, 162 addr, 163 datamatch, 164 register: true, 165 })) 166 } 167 168 /// Unregister an eventfd with raw guest memory address. unregister_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()>169 pub fn unregister_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()> { 170 self.request_unit(&VmMemoryRequest::IoEventRaw(IoEventUpdateRequest { 171 event, 172 addr, 173 datamatch, 174 register: false, 175 })) 176 } 177 balloon_target_reached(&self, size: u64) -> Result<()>178 pub fn balloon_target_reached(&self, size: u64) -> Result<()> { 179 self.request_unit(&VmMemoryRequest::BalloonTargetReached { size }) 180 } 181 } 182 183 impl AsRawDescriptor for VmMemoryClient { as_raw_descriptor(&self) -> RawDescriptor184 fn as_raw_descriptor(&self) -> RawDescriptor { 185 self.tube.as_raw_descriptor() 186 } 187 } 188