blob: 1aa9e191e3c73559eefd3885cba151fe8277815c [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#[cfg(feature = "gpu")]
pub(crate) mod gpu;
use std::path::Path;
use base::error;
use base::AsRawDescriptor;
use base::Descriptor;
use base::Error as SysError;
use base::MemoryMappingArena;
use base::MmapError;
use base::Protection;
use base::SafeDescriptor;
use base::Tube;
use base::UnixSeqpacket;
use hypervisor::MemSlot;
use hypervisor::Vm;
use libc::EINVAL;
use libc::ERANGE;
use resources::Alloc;
use resources::SystemAllocator;
use serde::Deserialize;
use serde::Serialize;
use vm_memory::GuestAddress;
use crate::client::HandleRequestResult;
use crate::VmRequest;
use crate::VmResponse;
pub fn handle_request<T: AsRef<Path> + std::fmt::Debug>(
request: &VmRequest,
socket_path: T,
) -> HandleRequestResult {
match UnixSeqpacket::connect(&socket_path) {
Ok(s) => {
let socket = Tube::new_from_unix_seqpacket(s);
if let Err(e) = socket.send(request) {
error!(
"failed to send request to socket at '{:?}': {}",
socket_path, e
);
return Err(());
}
match socket.recv() {
Ok(response) => Ok(response),
Err(e) => {
error!(
"failed to recv response from socket at '{:?}': {}",
socket_path, e
);
Err(())
}
}
}
Err(e) => {
error!("failed to connect to socket at '{:?}': {}", socket_path, e);
Err(())
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub enum VmMsyncRequest {
/// Flush the content of a memory mapping to its backing file.
/// `slot` selects the arena (as returned by `Vm::add_mmap_arena`).
/// `offset` is the offset of the mapping to sync within the arena.
/// `size` is the size of the mapping to sync within the arena.
MsyncArena {
slot: MemSlot,
offset: usize,
size: usize,
},
}
#[derive(Serialize, Deserialize, Debug)]
pub enum VmMsyncResponse {
Ok,
Err(SysError),
}
impl VmMsyncRequest {
/// Executes this request on the given Vm.
///
/// # Arguments
/// * `vm` - The `Vm` to perform the request on.
///
/// This does not return a result, instead encapsulating the success or failure in a
/// `VmMsyncResponse` with the intended purpose of sending the response back over the socket
/// that received this `VmMsyncResponse`.
pub fn execute(&self, vm: &mut impl Vm) -> VmMsyncResponse {
use self::VmMsyncRequest::*;
match *self {
MsyncArena { slot, offset, size } => match vm.msync_memory_region(slot, offset, size) {
Ok(()) => VmMsyncResponse::Ok,
Err(e) => VmMsyncResponse::Err(e),
},
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub enum FsMappingRequest {
/// Create an anonymous memory mapping that spans the entire region described by `Alloc`.
AllocateSharedMemoryRegion(Alloc),
/// Create a memory mapping.
CreateMemoryMapping {
/// The slot for a MemoryMappingArena, previously returned by a response to an
/// `AllocateSharedMemoryRegion` request.
slot: u32,
/// The file descriptor that should be mapped.
fd: SafeDescriptor,
/// The size of the mapping.
size: usize,
/// The offset into the file from where the mapping should start.
file_offset: u64,
/// The memory protection to be used for the mapping. Protections other than readable and
/// writable will be silently dropped.
prot: Protection,
/// The offset into the shared memory region where the mapping should be placed.
mem_offset: usize,
},
/// Remove a memory mapping.
RemoveMemoryMapping {
/// The slot for a MemoryMappingArena.
slot: u32,
/// The offset into the shared memory region.
offset: usize,
/// The size of the mapping.
size: usize,
},
}
impl FsMappingRequest {
pub fn execute(&self, vm: &mut dyn Vm, allocator: &mut SystemAllocator) -> VmResponse {
use self::FsMappingRequest::*;
match *self {
AllocateSharedMemoryRegion(Alloc::PciBar {
bus,
dev,
func,
bar,
}) => {
match allocator.mmio_allocator_any().get(&Alloc::PciBar {
bus,
dev,
func,
bar,
}) {
Some((range, _)) => {
let size: usize = match range.len().and_then(|x| x.try_into().ok()) {
Some(v) => v,
None => return VmResponse::Err(SysError::new(ERANGE)),
};
let arena = match MemoryMappingArena::new(size) {
Ok(a) => a,
Err(MmapError::SystemCallFailed(e)) => return VmResponse::Err(e),
_ => return VmResponse::Err(SysError::new(EINVAL)),
};
match vm.add_memory_region(
GuestAddress(range.start),
Box::new(arena),
false,
false,
) {
Ok(slot) => VmResponse::RegisterMemory {
pfn: range.start >> 12,
slot,
},
Err(e) => VmResponse::Err(e),
}
}
None => VmResponse::Err(SysError::new(EINVAL)),
}
}
CreateMemoryMapping {
slot,
ref fd,
size,
file_offset,
prot,
mem_offset,
} => {
let raw_fd: Descriptor = Descriptor(fd.as_raw_descriptor());
match vm.add_fd_mapping(slot, mem_offset, size, &raw_fd, file_offset, prot) {
Ok(()) => VmResponse::Ok,
Err(e) => VmResponse::Err(e),
}
}
RemoveMemoryMapping { slot, offset, size } => {
match vm.remove_mapping(slot, offset, size) {
Ok(()) => VmResponse::Ok,
Err(e) => VmResponse::Err(e),
}
}
_ => VmResponse::Err(SysError::new(EINVAL)),
}
}
}