Coverage Report

Created: 2026-05-16 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}