xref: /aosp_15_r20/external/crosvm/vm_control/src/api.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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