• 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("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