• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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