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