vm_control/
api.rs

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
7use base::AsRawDescriptor;
8use base::Event;
9use base::Protection;
10use base::RawDescriptor;
11use base::Tube;
12use base::TubeError;
13use hypervisor::Datamatch;
14use hypervisor::MemCacheType;
15use remain::sorted;
16use resources::Alloc;
17use serde::Deserialize;
18use serde::Serialize;
19use thiserror::Error;
20use vm_memory::GuestAddress;
21
22use crate::IoEventUpdateRequest;
23use crate::VmMemoryDestination;
24use crate::VmMemoryRegionId;
25use crate::VmMemoryRequest;
26use crate::VmMemoryResponse;
27use crate::VmMemoryResponseError;
28use crate::VmMemorySource;
29
30#[derive(Error, Debug)]
31#[sorted]
32pub 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
45pub type Result<T> = std::result::Result<T, ApiClientError>;
46
47#[derive(Serialize, Deserialize)]
48pub struct VmMemoryClient {
49    tube: Tube,
50}
51
52impl VmMemoryClient {
53    pub fn new(tube: Tube) -> Self {
54        VmMemoryClient { tube }
55    }
56
57    fn request(&self, request: &VmMemoryRequest) -> Result<VmMemoryResponse> {
58        self.tube.send(request).map_err(ApiClientError::Send)?;
59        self.tube
60            .recv::<VmMemoryResponse>()
61            .map_err(ApiClientError::Recv)
62    }
63
64    fn request_unit(&self, request: &VmMemoryRequest) -> Result<()> {
65        match self.request(request)? {
66            VmMemoryResponse::Ok => Ok(()),
67            VmMemoryResponse::Err(VmMemoryResponseError(e)) => {
68                Err(ApiClientError::RequestFailed(e))
69            }
70            _other => Err(ApiClientError::UnexpectedResponse),
71        }
72    }
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    pub fn prepare_shared_memory_region(&self, alloc: Alloc, cache: MemCacheType) -> Result<()> {
77        self.request_unit(&VmMemoryRequest::PrepareSharedMemoryRegion { alloc, cache })
78    }
79
80    pub fn register_memory(
81        &self,
82        source: VmMemorySource,
83        dest: VmMemoryDestination,
84        prot: Protection,
85        cache: MemCacheType,
86    ) -> Result<VmMemoryRegionId> {
87        let request = VmMemoryRequest::RegisterMemory {
88            source,
89            dest,
90            prot,
91            cache,
92        };
93        match self.request(&request)? {
94            VmMemoryResponse::Err(VmMemoryResponseError(e)) => {
95                Err(ApiClientError::RequestFailed(e))
96            }
97            VmMemoryResponse::RegisterMemory { region_id, .. } => Ok(region_id),
98            _other => Err(ApiClientError::UnexpectedResponse),
99        }
100    }
101
102    #[cfg(any(target_os = "android", target_os = "linux"))]
103    pub fn mmap_and_register_memory(
104        &self,
105        mapping_address: GuestAddress,
106        shm: base::SharedMemory,
107        file_mapping_info: Vec<crate::VmMemoryFileMapping>,
108    ) -> Result<u32> {
109        let num_file_mappings = file_mapping_info.len();
110        let req = VmMemoryRequest::MmapAndRegisterMemory {
111            shm,
112            dest: VmMemoryDestination::GuestPhysicalAddress(mapping_address.0),
113            num_file_mappings,
114        };
115
116        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        for m in file_mapping_info.chunks(base::unix::SCM_MAX_FD) {
122            self.tube
123                .send_with_max_fds(&m, m.len())
124                .map_err(ApiClientError::SendFds)?;
125        }
126
127        match self.tube.recv().map_err(ApiClientError::Recv)? {
128            VmMemoryResponse::RegisterMemory { slot, .. } => Ok(slot),
129            VmMemoryResponse::Err(VmMemoryResponseError(e)) => {
130                Err(ApiClientError::RequestFailed(e))
131            }
132            _ => Err(ApiClientError::UnexpectedResponse),
133        }
134    }
135
136    /// Call hypervisor to free the given memory ranges.
137    pub fn dynamically_free_memory_ranges(&self, ranges: Vec<(GuestAddress, u64)>) -> Result<()> {
138        self.request_unit(&VmMemoryRequest::DynamicallyFreeMemoryRanges { ranges })
139    }
140
141    /// Call hypervisor to reclaim a priorly freed memory range.
142    pub fn dynamically_reclaim_memory_ranges(
143        &self,
144        ranges: Vec<(GuestAddress, u64)>,
145    ) -> Result<()> {
146        self.request_unit(&VmMemoryRequest::DynamicallyReclaimMemoryRanges { ranges })
147    }
148
149    /// Unregister the given memory slot that was previously registered with `RegisterMemory`.
150    pub fn unregister_memory(&self, region: VmMemoryRegionId) -> Result<()> {
151        self.request_unit(&VmMemoryRequest::UnregisterMemory(region))
152    }
153
154    /// Register an eventfd with raw guest memory address.
155    pub fn register_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()> {
156        self.request_unit(&VmMemoryRequest::IoEventRaw(IoEventUpdateRequest {
157            event,
158            addr,
159            datamatch,
160            register: true,
161        }))
162    }
163
164    /// Unregister an eventfd with raw guest memory address.
165    pub fn unregister_io_event(&self, event: Event, addr: u64, datamatch: Datamatch) -> Result<()> {
166        self.request_unit(&VmMemoryRequest::IoEventRaw(IoEventUpdateRequest {
167            event,
168            addr,
169            datamatch,
170            register: false,
171        }))
172    }
173
174    pub fn balloon_target_reached(&self, size: u64) -> Result<()> {
175        self.request_unit(&VmMemoryRequest::BalloonTargetReached { size })
176    }
177}
178
179impl AsRawDescriptor for VmMemoryClient {
180    fn as_raw_descriptor(&self) -> RawDescriptor {
181        self.tube.as_raw_descriptor()
182    }
183}