vm_control/sys/
linux.rs

1// Copyright 2022 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#[cfg(feature = "gpu")]
6pub(crate) mod gpu;
7
8use std::path::Path;
9use std::sync::LazyLock;
10use std::time::Duration;
11
12use base::error;
13use base::AsRawDescriptor;
14use base::Descriptor;
15use base::Error as SysError;
16use base::MemoryMappingArena;
17use base::MmapError;
18use base::Protection;
19use base::SafeDescriptor;
20use base::Tube;
21use base::UnixSeqpacket;
22use hypervisor::MemCacheType;
23use hypervisor::MemSlot;
24use hypervisor::Vm;
25use libc::EINVAL;
26use libc::ERANGE;
27use resources::Alloc;
28use resources::SystemAllocator;
29use serde::Deserialize;
30use serde::Serialize;
31use vm_memory::GuestAddress;
32
33use crate::client::HandleRequestResult;
34use crate::VmMappedMemoryRegion;
35use crate::VmRequest;
36use crate::VmResponse;
37
38pub fn handle_request<T: AsRef<Path> + std::fmt::Debug>(
39    request: &VmRequest,
40    socket_path: T,
41) -> HandleRequestResult {
42    handle_request_with_timeout(request, socket_path, None)
43}
44
45pub fn handle_request_with_timeout<T: AsRef<Path> + std::fmt::Debug>(
46    request: &VmRequest,
47    socket_path: T,
48    timeout: Option<Duration>,
49) -> HandleRequestResult {
50    match UnixSeqpacket::connect(&socket_path) {
51        Ok(s) => {
52            let socket = Tube::try_from(s).map_err(|_| ())?;
53            if timeout.is_some() {
54                if let Err(e) = socket.set_recv_timeout(timeout) {
55                    error!(
56                        "failed to set recv timeout on socket at '{:?}': {}",
57                        socket_path, e
58                    );
59                    return Err(());
60                }
61            }
62            if let Err(e) = socket.send(request) {
63                error!(
64                    "failed to send request to socket at '{:?}': {}",
65                    socket_path, e
66                );
67                return Err(());
68            }
69            match socket.recv() {
70                Ok(response) => Ok(response),
71                Err(e) => {
72                    error!(
73                        "failed to recv response from socket at '{:?}': {}",
74                        socket_path, e
75                    );
76                    Err(())
77                }
78            }
79        }
80        Err(e) => {
81            error!("failed to connect to socket at '{:?}': {}", socket_path, e);
82            Err(())
83        }
84    }
85}
86
87#[derive(Serialize, Deserialize, Debug)]
88pub enum VmMemoryMappingRequest {
89    /// Flush the content of a memory mapping to its backing file.
90    /// `slot` selects the arena (as returned by `Vm::add_mmap_arena`).
91    /// `offset` is the offset of the mapping to sync within the arena.
92    /// `size` is the size of the mapping to sync within the arena.
93    MsyncArena {
94        slot: MemSlot,
95        offset: usize,
96        size: usize,
97    },
98
99    /// Gives a MADV_PAGEOUT advice to the memory region mapped at `slot`, with the address range
100    /// starting at `offset` from the start of the region, and with size `size`.
101    MadvisePageout {
102        slot: MemSlot,
103        offset: usize,
104        size: usize,
105    },
106
107    /// Gives a MADV_REMOVE advice to the memory region mapped at `slot`, with the address range
108    /// starting at `offset` from the start of the region, and with size `size`.
109    MadviseRemove {
110        slot: MemSlot,
111        offset: usize,
112        size: usize,
113    },
114}
115
116#[derive(Serialize, Deserialize, Debug)]
117pub enum VmMemoryMappingResponse {
118    Ok,
119    Err(SysError),
120}
121
122impl VmMemoryMappingRequest {
123    /// Executes this request on the given Vm.
124    ///
125    /// # Arguments
126    /// * `vm` - The `Vm` to perform the request on.
127    ///
128    /// This does not return a result, instead encapsulating the success or failure in a
129    /// `VmMsyncResponse` with the intended purpose of sending the response back over the socket
130    /// that received this `VmMsyncResponse`.
131    pub fn execute(&self, vm: &mut impl Vm) -> VmMemoryMappingResponse {
132        use self::VmMemoryMappingRequest::*;
133        match *self {
134            MsyncArena { slot, offset, size } => match vm.msync_memory_region(slot, offset, size) {
135                Ok(()) => VmMemoryMappingResponse::Ok,
136                Err(e) => VmMemoryMappingResponse::Err(e),
137            },
138            MadvisePageout { slot, offset, size } => {
139                match vm.madvise_pageout_memory_region(slot, offset, size) {
140                    Ok(()) => VmMemoryMappingResponse::Ok,
141                    Err(e) => VmMemoryMappingResponse::Err(e),
142                }
143            }
144            MadviseRemove { slot, offset, size } => {
145                match vm.madvise_remove_memory_region(slot, offset, size) {
146                    Ok(()) => VmMemoryMappingResponse::Ok,
147                    Err(e) => VmMemoryMappingResponse::Err(e),
148                }
149            }
150        }
151    }
152}
153
154#[derive(Serialize, Deserialize, Debug)]
155pub enum FsMappingRequest {
156    /// Create an anonymous memory mapping that spans the entire region described by `Alloc`.
157    AllocateSharedMemoryRegion(Alloc),
158    /// Create a memory mapping.
159    CreateMemoryMapping {
160        /// The slot for a MemoryMappingArena, previously returned by a response to an
161        /// `AllocateSharedMemoryRegion` request.
162        slot: u32,
163        /// The file descriptor that should be mapped.
164        fd: SafeDescriptor,
165        /// The size of the mapping.
166        size: usize,
167        /// The offset into the file from where the mapping should start.
168        file_offset: u64,
169        /// The memory protection to be used for the mapping.  Protections other than readable and
170        /// writable will be silently dropped.
171        prot: Protection,
172        /// The offset into the shared memory region where the mapping should be placed.
173        mem_offset: usize,
174    },
175    /// Remove a memory mapping.
176    RemoveMemoryMapping {
177        /// The slot for a MemoryMappingArena.
178        slot: u32,
179        /// The offset into the shared memory region.
180        offset: usize,
181        /// The size of the mapping.
182        size: usize,
183    },
184}
185
186pub fn prepare_shared_memory_region(
187    vm: &mut dyn Vm,
188    allocator: &mut SystemAllocator,
189    alloc: Alloc,
190    cache: MemCacheType,
191) -> Result<VmMappedMemoryRegion, SysError> {
192    if !matches!(alloc, Alloc::PciBar { .. }) {
193        return Err(SysError::new(EINVAL));
194    }
195    match allocator.mmio_allocator_any().get(&alloc) {
196        Some((range, _)) => {
197            let size: usize = match range.len().and_then(|x| x.try_into().ok()) {
198                Some(v) => v,
199                None => return Err(SysError::new(ERANGE)),
200            };
201            let arena = match MemoryMappingArena::new(size) {
202                Ok(a) => a,
203                Err(MmapError::SystemCallFailed(e)) => return Err(e),
204                _ => return Err(SysError::new(EINVAL)),
205            };
206
207            match vm.add_memory_region(
208                GuestAddress(range.start),
209                Box::new(arena),
210                false,
211                false,
212                cache,
213            ) {
214                Ok(slot) => Ok(VmMappedMemoryRegion {
215                    guest_address: GuestAddress(range.start),
216                    slot,
217                }),
218                Err(e) => Err(e),
219            }
220        }
221        None => Err(SysError::new(EINVAL)),
222    }
223}
224
225static SHOULD_PREPARE_MEMORY_REGION: LazyLock<bool> = LazyLock::new(|| {
226    if cfg!(target_arch = "x86_64") {
227        // The legacy x86 MMU allocates an rmap and a page tracking array
228        // that take 2.5MiB per 1GiB of user memory region address space,
229        // so avoid mapping the whole shared memory region if we're not
230        // using the tdp mmu.
231        match std::fs::read("/sys/module/kvm/parameters/tdp_mmu") {
232            Ok(bytes) if !bytes.is_empty() => bytes[0] == b'Y',
233            _ => false,
234        }
235    } else if cfg!(target_pointer_width = "64") {
236        true
237    } else {
238        // Not enough address space on 32-bit systems
239        false
240    }
241});
242
243pub fn should_prepare_memory_region() -> bool {
244    *SHOULD_PREPARE_MEMORY_REGION
245}
246
247impl FsMappingRequest {
248    pub fn execute(&self, vm: &mut dyn Vm, allocator: &mut SystemAllocator) -> VmResponse {
249        use self::FsMappingRequest::*;
250        match *self {
251            AllocateSharedMemoryRegion(alloc) => {
252                match prepare_shared_memory_region(
253                    vm,
254                    allocator,
255                    alloc,
256                    MemCacheType::CacheCoherent,
257                ) {
258                    Ok(VmMappedMemoryRegion { slot, .. }) => VmResponse::RegisterMemory { slot },
259                    Err(e) => VmResponse::Err(e),
260                }
261            }
262            CreateMemoryMapping {
263                slot,
264                ref fd,
265                size,
266                file_offset,
267                prot,
268                mem_offset,
269            } => {
270                let raw_fd: Descriptor = Descriptor(fd.as_raw_descriptor());
271
272                match vm.add_fd_mapping(slot, mem_offset, size, &raw_fd, file_offset, prot) {
273                    Ok(()) => VmResponse::Ok,
274                    Err(e) => VmResponse::Err(e),
275                }
276            }
277            RemoveMemoryMapping { slot, offset, size } => {
278                match vm.remove_mapping(slot, offset, size) {
279                    Ok(()) => VmResponse::Ok,
280                    Err(e) => VmResponse::Err(e),
281                }
282            }
283        }
284    }
285}