base: Add shared memory layer to base.

This is very largely boilerplate, but changes some interfaces
to accept different parameters, esp using Descriptor.

BUG=b:162363783
TEST=./build_test

Change-Id: I81b513c0de9448c2de505fe5d3a61a29f4eccb78
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2342591
Reviewed-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Michael Hoyle <mikehoyle@google.com>
This commit is contained in:
Michael Hoyle 2020-08-05 18:50:47 -07:00 committed by Commit Bot
parent 48537d43b6
commit a7e38ab421
30 changed files with 426 additions and 120 deletions

4
Cargo.lock generated
View file

@ -76,6 +76,7 @@ dependencies = [
name = "base"
version = "0.1.0"
dependencies = [
"data_model 0.1.0",
"sys_util 0.1.0",
]
@ -166,6 +167,7 @@ dependencies = [
"remain 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"resources 0.1.0",
"sync 0.1.0",
"tempfile 3.0.7",
"vhost 0.1.0",
"vm_control 0.1.0",
"vm_memory 0.1.0",
@ -249,6 +251,7 @@ dependencies = [
"protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"protos 0.1.0",
"remain 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.0.7",
"vm_memory 0.1.0",
]
@ -418,6 +421,7 @@ version = "0.1.0"
dependencies = [
"base 0.1.0",
"libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.0.7",
"vm_memory 0.1.0",
]

View file

@ -75,6 +75,7 @@ rand_ish = { path = "rand_ish" }
remain = "*"
resources = { path = "resources" }
sync = { path = "sync" }
tempfile = "*"
vhost = { path = "vhost" }
vm_control = { path = "vm_control" }
acpi_tables = { path = "acpi_tables" }

View file

@ -17,7 +17,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use acpi_tables::sdt::SDT;
use base::{syslog, EventFd};
use base::{syslog, AsRawDescriptor, EventFd};
use devices::virtio::VirtioDevice;
use devices::{
Bus, BusDevice, BusError, IrqChip, PciAddress, PciDevice, PciDeviceError, PciInterruptPin,
@ -373,7 +373,7 @@ pub fn load_image<F>(
max_size: u64,
) -> Result<usize, LoadImageError>
where
F: Read + Seek + AsRawFd,
F: Read + Seek + AsRawDescriptor,
{
let size = image.seek(SeekFrom::End(0)).map_err(LoadImageError::Seek)?;
@ -415,7 +415,7 @@ pub fn load_image_high<F>(
align: u64,
) -> Result<(GuestAddress, usize), LoadImageError>
where
F: Read + Seek + AsRawFd,
F: Read + Seek + AsRawDescriptor,
{
if !align.is_power_of_two() {
return Err(LoadImageError::BadAlignment(align));

View file

@ -63,7 +63,7 @@ pub fn create_memory_region(
.map_err(Error::ResourcesError)?;
let memory_mapping =
MemoryMapping::from_fd(&file, pstore.size as usize).map_err(Error::MmapError)?;
MemoryMapping::from_descriptor(&file, pstore.size as usize).map_err(Error::MmapError)?;
vm.add_memory_region(
GuestAddress(address),

View file

@ -8,4 +8,5 @@ edition = "2018"
chromeos = ["sys_util/chromeos"]
[dependencies]
data_model = { path = "../data_model" }
sys_util = { path = "../sys_util" }

View file

@ -3,3 +3,15 @@
// found in the LICENSE file.
pub use sys_util::*;
mod mmap;
mod shm;
pub use mmap::MemoryMapping;
pub use shm::{SharedMemory, Unix as SharedMemoryUnix};
/// Wraps an AsRawDescriptor in the simple Descriptor struct, which
/// has AsRawFd methods for interfacing with sys_util
fn wrap_descriptor(descriptor: &dyn AsRawDescriptor) -> Descriptor {
Descriptor(descriptor.as_raw_descriptor())
}

142
base/src/mmap.rs Normal file
View file

@ -0,0 +1,142 @@
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::{wrap_descriptor, AsRawDescriptor, MappedRegion, MmapError, Protection};
use data_model::volatile_memory::*;
use data_model::DataInit;
use sys_util::MemoryMapping as SysUtilMmap;
pub type Result<T> = std::result::Result<T, MmapError>;
/// See [MemoryMapping](sys_util::MemoryMapping) for struct- and method-level
/// documentation.
#[derive(Debug)]
pub struct MemoryMapping(SysUtilMmap);
impl MemoryMapping {
pub fn new(size: usize) -> Result<MemoryMapping> {
SysUtilMmap::new(size).map(|mmap| MemoryMapping(mmap))
}
pub fn from_descriptor(descriptor: &dyn AsRawDescriptor, size: usize) -> Result<MemoryMapping> {
SysUtilMmap::from_fd(&wrap_descriptor(descriptor), size).map(|mmap| MemoryMapping(mmap))
}
pub fn from_descriptor_offset(
descriptor: &dyn AsRawDescriptor,
size: usize,
offset: u64,
) -> Result<MemoryMapping> {
SysUtilMmap::from_fd_offset(&wrap_descriptor(descriptor), size, offset)
.map(|mmap| MemoryMapping(mmap))
}
pub fn new_protection(size: usize, prot: Protection) -> Result<MemoryMapping> {
SysUtilMmap::new_protection(size, prot).map(|mmap| MemoryMapping(mmap))
}
pub fn from_descriptor_offset_protection(
descriptor: &dyn AsRawDescriptor,
size: usize,
offset: u64,
prot: Protection,
) -> Result<MemoryMapping> {
SysUtilMmap::from_fd_offset_protection(&wrap_descriptor(descriptor), size, offset, prot)
.map(|mmap| MemoryMapping(mmap))
}
pub unsafe fn new_protection_fixed(
addr: *mut u8,
size: usize,
prot: Protection,
) -> Result<MemoryMapping> {
SysUtilMmap::new_protection_fixed(addr, size, prot).map(|mmap| MemoryMapping(mmap))
}
pub unsafe fn from_descriptor_offset_protection_fixed(
addr: *mut u8,
descriptor: &dyn AsRawDescriptor,
size: usize,
offset: u64,
prot: Protection,
) -> Result<MemoryMapping> {
SysUtilMmap::from_fd_offset_protection_fixed(
addr,
&wrap_descriptor(descriptor),
size,
offset,
prot,
)
.map(|mmap| MemoryMapping(mmap))
}
pub fn from_descriptor_offset_populate(
descriptor: &dyn AsRawDescriptor,
size: usize,
offset: u64,
) -> Result<MemoryMapping> {
SysUtilMmap::from_fd_offset_populate(&wrap_descriptor(descriptor), size, offset)
.map(|mmap| MemoryMapping(mmap))
}
pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> {
self.0.write_slice(buf, offset)
}
pub fn read_slice(&self, buf: &mut [u8], offset: usize) -> Result<usize> {
self.0.read_slice(buf, offset)
}
pub fn write_obj<T: DataInit>(&self, val: T, offset: usize) -> Result<()> {
self.0.write_obj(val, offset)
}
pub fn read_obj<T: DataInit>(&self, offset: usize) -> Result<T> {
self.0.read_obj(offset)
}
pub fn msync(&self) -> Result<()> {
self.0.msync()
}
pub fn read_to_memory(
&self,
mem_offset: usize,
src: &dyn AsRawDescriptor,
count: usize,
) -> Result<()> {
self.0
.read_to_memory(mem_offset, &wrap_descriptor(src), count)
}
pub fn write_from_memory(
&self,
mem_offset: usize,
dst: &dyn AsRawDescriptor,
count: usize,
) -> Result<()> {
self.0
.write_from_memory(mem_offset, &wrap_descriptor(dst), count)
}
pub fn remove_range(&self, mem_offset: usize, count: usize) -> Result<()> {
self.0.remove_range(mem_offset, count)
}
}
impl VolatileMemory for MemoryMapping {
fn get_slice(&self, offset: usize, count: usize) -> VolatileMemoryResult<VolatileSlice> {
self.0.get_slice(offset, count)
}
}
// Safe because it exclusively forwards calls to a safe implementation.
unsafe impl MappedRegion for MemoryMapping {
fn as_ptr(&self) -> *mut u8 {
self.0.as_ptr()
}
fn size(&self) -> usize {
self.0.size()
}
}

86
base/src/shm.rs Normal file
View file

@ -0,0 +1,86 @@
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::{
AsRawDescriptor, FromRawDescriptor, IntoRawDescriptor, MemfdSeals, RawDescriptor, Result,
SafeDescriptor,
};
use std::ffi::CStr;
use std::fs::File;
use std::os::unix::io::{AsRawFd, RawFd};
use sys_util::SharedMemory as SysUtilSharedMemory;
/// See [SharedMemory](sys_util::SharedMemory) for struct- and method-level
/// documentation.
pub struct SharedMemory(SysUtilSharedMemory);
impl SharedMemory {
pub fn named<T: Into<Vec<u8>>>(name: T, size: u64) -> Result<SharedMemory> {
SysUtilSharedMemory::named(name)
.and_then(|mut shm| shm.set_size(size).map(|_| shm))
.map(|shm| SharedMemory(shm))
}
pub fn anon(size: u64) -> Result<SharedMemory> {
Self::new(None, size)
}
pub fn new(name: Option<&CStr>, size: u64) -> Result<SharedMemory> {
SysUtilSharedMemory::new(name)
.and_then(|mut shm| shm.set_size(size).map(|_| shm))
.map(|shm| SharedMemory(shm))
}
pub fn size(&self) -> u64 {
self.0.size()
}
/// Unwraps the sys_util::SharedMemory stored within this type.
/// This should be used only when necessary for interacting with
/// external libraries.
pub fn inner(&self) -> &SysUtilSharedMemory {
&self.0
}
}
pub trait Unix {
/// Creates a SharedMemory instance from a SafeDescriptor owning a reference to a
/// shared memory descriptor. Ownership of the underlying descriptor is transferred to the
/// new SharedMemory object.
fn from_safe_descriptor(descriptor: SafeDescriptor) -> Result<SharedMemory> {
let file = unsafe { File::from_raw_descriptor(descriptor.into_raw_descriptor()) };
SysUtilSharedMemory::from_file(file).map(|shm| SharedMemory(shm))
}
fn from_file(file: File) -> Result<SharedMemory> {
SysUtilSharedMemory::from_file(file).map(|shm| SharedMemory(shm))
}
fn get_seals(&self) -> Result<MemfdSeals>;
fn add_seals(&mut self, seals: MemfdSeals) -> Result<()>;
}
impl Unix for SharedMemory {
fn get_seals(&self) -> Result<MemfdSeals> {
self.0.get_seals()
}
fn add_seals(&mut self, seals: MemfdSeals) -> Result<()> {
self.0.add_seals(seals)
}
}
// TODO(mikehoyle): Remove this in favor of just AsRawDescriptor
// when the rest of the codebase is ready.
impl AsRawFd for SharedMemory {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl AsRawDescriptor for SharedMemory {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.0.as_raw_fd()
}
}

View file

@ -550,7 +550,7 @@ impl Ac97BusMaster {
DEVICE_SAMPLE_RATE,
buffer_frames,
&Self::stream_effects(func),
self.mem.as_ref(),
self.mem.as_ref().inner(),
starting_offsets,
)
.map_err(AudioError::CreateStream)?;

View file

@ -740,7 +740,7 @@ impl VfioPciDevice {
// Even if vm has mapped this region, but it is in vm main process,
// device process doesn't has this mapping, but vfio_dma_map() need it
// in device process, so here map it again.
let mmap = match MemoryMapping::from_fd_offset(
let mmap = match MemoryMapping::from_descriptor_offset(
self.device.as_ref(),
mmap_size as usize,
offset,

View file

@ -17,8 +17,8 @@ use std::u32;
use sync::Mutex;
use base::{
ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, warn, Error,
EventFd, SafeDescriptor,
ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref, ioctl_with_val, warn,
AsRawDescriptor, Error, EventFd, RawDescriptor, SafeDescriptor,
};
use hypervisor::{DeviceKind, Vm};
use vm_memory::GuestMemory;
@ -807,8 +807,15 @@ impl VfioDevice {
}
}
// TODO(mikehoyle): Remove this in favor of AsRawDescriptor
impl AsRawFd for VfioDevice {
fn as_raw_fd(&self) -> RawFd {
self.dev.as_raw_fd()
}
}
impl AsRawDescriptor for VfioDevice {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.dev.as_raw_descriptor()
}
}

View file

@ -56,8 +56,8 @@ use data_model::*;
#[cfg(feature = "wl-dmabuf")]
use base::ioctl_iow_nr;
use base::{
error, pipe, round_up_to_page_size, warn, Error, EventFd, FileFlags, PollContext, PollToken,
Result, ScmSocket, SharedMemory,
error, pipe, round_up_to_page_size, warn, AsRawDescriptor, Error, EventFd, FileFlags,
PollContext, PollToken, Result, ScmSocket, SharedMemory, SharedMemoryUnix,
};
use msg_socket::{MsgError, MsgReceiver, MsgSender};
#[cfg(feature = "wl-dmabuf")]
@ -263,7 +263,6 @@ fn encode_resp(writer: &mut Writer, resp: WlResp) -> WlResult<()> {
enum WlError {
NewAlloc(Error),
NewPipe(Error),
AllocSetSize(Error),
SocketConnect(io::Error),
SocketNonBlock(io::Error),
VmControl(MsgError),
@ -278,6 +277,7 @@ enum WlError {
ReadPipe(io::Error),
PollContextAdd(Error),
DmabufSync(io::Error),
FromSharedMemory(Error),
WriteResponse(io::Error),
InvalidString(std::str::Utf8Error),
UnknownSocketName(String),
@ -290,7 +290,6 @@ impl Display for WlError {
match self {
NewAlloc(e) => write!(f, "failed to create shared memory allocation: {}", e),
NewPipe(e) => write!(f, "failed to create pipe: {}", e),
AllocSetSize(e) => write!(f, "failed to set size of shared memory: {}", e),
SocketConnect(e) => write!(f, "failed to connect socket: {}", e),
SocketNonBlock(e) => write!(f, "failed to set socket as non-blocking: {}", e),
VmControl(e) => write!(f, "failed to control parent VM: {}", e),
@ -305,6 +304,9 @@ impl Display for WlError {
ReadPipe(e) => write!(f, "failed to read a pipe: {}", e),
PollContextAdd(e) => write!(f, "failed to listen to FD on poll context: {}", e),
DmabufSync(e) => write!(f, "failed to synchronize DMABuf access: {}", e),
FromSharedMemory(e) => {
write!(f, "failed to create shared memory from descriptor: {}", e)
}
WriteResponse(e) => write!(f, "failed to write response: {}", e),
InvalidString(e) => write!(f, "invalid string: {}", e),
UnknownSocketName(name) => write!(f, "unknown socket name: {}", name),
@ -519,7 +521,7 @@ impl<'a> WlResp<'a> {
#[derive(Default)]
struct WlVfd {
socket: Option<UnixStream>,
guest_shared_memory: Option<(u64 /* size */, File)>,
guest_shared_memory: Option<(u64 /* size */, SharedMemory)>,
remote_pipe: Option<File>,
local_pipe: Option<(u32 /* flags */, File)>,
slot: Option<(MemSlot, u64 /* pfn */, VmRequester)>,
@ -556,18 +558,17 @@ impl WlVfd {
fn allocate(vm: VmRequester, size: u64) -> WlResult<WlVfd> {
let size_page_aligned = round_up_to_page_size(size as usize) as u64;
let mut vfd_shm = SharedMemory::named("virtwl_alloc").map_err(WlError::NewAlloc)?;
vfd_shm
.set_size(size_page_aligned)
.map_err(WlError::AllocSetSize)?;
let vfd_shm =
SharedMemory::named("virtwl_alloc", size_page_aligned).map_err(WlError::NewAlloc)?;
let register_response = vm.request(VmMemoryRequest::RegisterMemory(
MaybeOwnedFd::Borrowed(vfd_shm.as_raw_fd()),
MaybeOwnedFd::Borrowed(vfd_shm.as_raw_descriptor()),
vfd_shm.size() as usize,
))?;
match register_response {
VmMemoryResponse::RegisterMemory { pfn, slot } => {
let mut vfd = WlVfd::default();
vfd.guest_shared_memory = Some((vfd_shm.size(), vfd_shm.into()));
vfd.guest_shared_memory = Some((vfd_shm.size(), vfd_shm));
vfd.slot = Some((slot, pfn, vm));
Ok(vfd)
}
@ -597,7 +598,7 @@ impl WlVfd {
} => {
let mut vfd = WlVfd::default();
let vfd_shm = SharedMemory::from_file(file).map_err(WlError::NewAlloc)?;
vfd.guest_shared_memory = Some((vfd_shm.size(), vfd_shm.into()));
vfd.guest_shared_memory = Some((vfd_shm.size(), vfd_shm));
vfd.slot = Some((slot, pfn, vm));
vfd.is_dmabuf = true;
Ok((vfd, desc))
@ -661,7 +662,10 @@ impl WlVfd {
match register_response {
VmMemoryResponse::RegisterMemory { pfn, slot } => {
let mut vfd = WlVfd::default();
vfd.guest_shared_memory = Some((size, fd));
vfd.guest_shared_memory = Some((
size,
SharedMemory::from_file(fd).map_err(WlError::FromSharedMemory)?,
));
vfd.slot = Some((slot, pfn, vm));
Ok(vfd)
}
@ -716,7 +720,7 @@ impl WlVfd {
fn send_fd(&self) -> Option<RawFd> {
self.guest_shared_memory
.as_ref()
.map(|(_, fd)| fd.as_raw_fd())
.map(|(_, fd)| fd.as_raw_descriptor())
.or(self.socket.as_ref().map(|s| s.as_raw_fd()))
.or(self.remote_pipe.as_ref().map(|p| p.as_raw_fd()))
}

View file

@ -16,6 +16,7 @@ base = { path = "../base" }
libc = "*"
protobuf = { version = "2.3", optional = true }
remain = "*"
tempfile = "*"
cros_async = { path = "../cros_async" }
data_model = { path = "../data_model" }
protos = { path = "../protos", optional = true }

View file

@ -329,8 +329,8 @@ impl FileReadWriteAtVolatile for AndroidSparse {
#[cfg(test)]
mod tests {
use super::*;
use base::SharedMemory;
use std::io::{Cursor, Write};
use tempfile::tempfile;
const CHUNK_SIZE: usize = mem::size_of::<ChunkHeader>();
@ -419,7 +419,7 @@ mod tests {
}
fn test_image(chunks: Vec<ChunkWithSize>) -> AndroidSparse {
let file: File = SharedMemory::anon().unwrap().into();
let file = tempfile().expect("failed to create tempfile");
let size = chunks.iter().map(|x| x.expanded_size).sum();
AndroidSparse::from_parts(file, size, chunks).expect("Could not create image")
}

View file

@ -1761,9 +1761,9 @@ fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 {
#[cfg(test)]
mod tests {
use super::*;
use base::{SharedMemory, WriteZeroes};
use std::fs::File;
use base::WriteZeroes;
use std::io::{Read, Seek, SeekFrom, Write};
use tempfile::tempfile;
fn valid_header() -> Vec<u8> {
vec![
@ -1813,8 +1813,7 @@ mod tests {
}
fn basic_file(header: &[u8]) -> File {
let shm = SharedMemory::anon().unwrap();
let mut disk_file: File = shm.into();
let mut disk_file = tempfile().expect("failed to create tempfile");
disk_file.write_all(&header).unwrap();
disk_file.set_len(0x8000_0000).unwrap();
disk_file.seek(SeekFrom::Start(0)).unwrap();
@ -1832,8 +1831,8 @@ mod tests {
where
F: FnMut(QcowFile),
{
let shm = SharedMemory::anon().unwrap();
let qcow_file = QcowFile::new(shm.into(), file_size).unwrap();
let file = tempfile().expect("failed to create tempfile");
let qcow_file = QcowFile::new(file, file_size).unwrap();
testfn(qcow_file); // File closed when the function exits.
}
@ -1841,8 +1840,7 @@ mod tests {
#[test]
fn default_header() {
let header = QcowHeader::create_for_size_and_path(0x10_0000, None);
let shm = SharedMemory::anon().unwrap();
let mut disk_file: File = shm.into();
let mut disk_file = tempfile().expect("failed to create tempfile");
header
.expect("Failed to create header.")
.write_to(&mut disk_file)
@ -1862,8 +1860,7 @@ mod tests {
fn header_with_backing() {
let header = QcowHeader::create_for_size_and_path(0x10_0000, Some("/my/path/to/a/file"))
.expect("Failed to create header.");
let shm = SharedMemory::anon().unwrap();
let mut disk_file: File = shm.into();
let mut disk_file = tempfile().expect("failed to create tempfile");
header
.write_to(&mut disk_file)
.expect("Failed to write header to shm.");

View file

@ -21,7 +21,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
use std::path::Path;
use std::ptr::{null, null_mut};
use base::{round_up_to_page_size, MemoryMapping, SharedMemory};
use base::{round_up_to_page_size, AsRawDescriptor, MemoryMapping, SharedMemory};
use data_model::VolatileMemory;
const BUFFER_COUNT: usize = 3;
@ -204,12 +204,9 @@ impl DisplayT for DisplayWl {
let row_size = width * BYTES_PER_PIXEL;
let fb_size = row_size * height;
let buffer_size = round_up_to_page_size(fb_size as usize * BUFFER_COUNT);
let mut buffer_shm =
SharedMemory::named("GpuDisplaySurface").map_err(GpuDisplayError::CreateShm)?;
buffer_shm
.set_size(buffer_size as u64)
.map_err(GpuDisplayError::SetSize)?;
let buffer_mem = MemoryMapping::from_fd(&buffer_shm, buffer_size).unwrap();
let buffer_shm = SharedMemory::named("GpuDisplaySurface", buffer_size as u64)
.map_err(GpuDisplayError::CreateShm)?;
let buffer_mem = MemoryMapping::from_descriptor(&buffer_shm, buffer_size).unwrap();
// Safe because only a valid context, parent pointer (if not None), and buffer FD are used.
// The returned surface is checked for validity before being filed away.
@ -217,7 +214,7 @@ impl DisplayT for DisplayWl {
dwl_context_surface_new(
self.ctx(),
parent_ptr,
buffer_shm.as_raw_fd(),
buffer_shm.as_raw_descriptor(),
buffer_size,
fb_size as usize,
width,

View file

@ -31,8 +31,6 @@ pub enum GpuDisplayError {
CreateEventFd,
/// Creating shared memory failed.
CreateShm(SysError),
/// Setting the size of shared memory failed.
SetSize(SysError),
/// Failed to create a surface on the compositor.
CreateSurface,
/// Failed to import a buffer to the compositor.
@ -61,7 +59,6 @@ impl Display for GpuDisplayError {
InvalidPath => write!(f, "invalid path"),
InvalidSurfaceId => write!(f, "invalid surface ID"),
RequiredFeature(feature) => write!(f, "required feature was missing: {}", feature),
SetSize(e) => write!(f, "failed to set size of shared memory: {}", e),
Unsupported => write!(f, "unsupported by the implementation"),
}
}

View file

@ -227,7 +227,7 @@ impl KvmVm {
let vcpu = unsafe { SafeDescriptor::from_raw_descriptor(fd) };
let run_mmap =
MemoryMapping::from_fd(&vcpu, run_mmap_size).map_err(|_| Error::new(ENOSPC))?;
MemoryMapping::from_descriptor(&vcpu, run_mmap_size).map_err(|_| Error::new(ENOSPC))?;
Ok(KvmVcpu {
vm: self.vm.try_clone()?,
@ -607,8 +607,8 @@ impl Vcpu for KvmVcpu {
fn try_clone(&self) -> Result<Self> {
let vm = self.vm.try_clone()?;
let vcpu = self.vcpu.try_clone()?;
let run_mmap =
MemoryMapping::from_fd(&vcpu, self.run_mmap.size()).map_err(|_| Error::new(ENOSPC))?;
let run_mmap = MemoryMapping::from_descriptor(&vcpu, self.run_mmap.size())
.map_err(|_| Error::new(ENOSPC))?;
Ok(KvmVcpu { vm, vcpu, run_mmap })
}

View file

@ -121,7 +121,7 @@ impl URingContext {
// Safe because we trust the kernel to set valid sizes in `io_uring_setup` and any error
// is checked.
let submit_ring = SubmitQueueState::new(
MemoryMapping::from_fd_offset_populate(
MemoryMapping::from_descriptor_offset_populate(
&ring_file,
ring_params.sq_off.array as usize
+ ring_params.sq_entries as usize * std::mem::size_of::<u32>(),
@ -133,7 +133,7 @@ impl URingContext {
let num_sqe = ring_params.sq_entries as usize;
let submit_queue_entries = SubmitQueueEntries {
mmap: MemoryMapping::from_fd_offset_populate(
mmap: MemoryMapping::from_descriptor_offset_populate(
&ring_file,
ring_params.sq_entries as usize * std::mem::size_of::<io_uring_sqe>(),
u64::from(IORING_OFF_SQES),
@ -143,7 +143,7 @@ impl URingContext {
};
let complete_ring = CompleteQueueState::new(
MemoryMapping::from_fd_offset_populate(
MemoryMapping::from_descriptor_offset_populate(
&ring_file,
ring_params.cq_off.cqes as usize
+ ring_params.cq_entries as usize * std::mem::size_of::<io_uring_cqe>(),

View file

@ -7,3 +7,6 @@ edition = "2018"
libc = "*"
base = { path = "../base" }
vm_memory = { path = "../vm_memory" }
[dev-dependencies]
tempfile = "*"

View file

@ -6,8 +6,8 @@ use std::ffi::CStr;
use std::fmt::{self, Display};
use std::io::{Read, Seek, SeekFrom};
use std::mem;
use std::os::unix::io::AsRawFd;
use base::AsRawDescriptor;
use vm_memory::{GuestAddress, GuestMemory};
#[allow(dead_code)]
@ -76,7 +76,7 @@ pub fn load_kernel<F>(
kernel_image: &mut F,
) -> Result<u64>
where
F: Read + Seek + AsRawFd,
F: Read + Seek + AsRawDescriptor,
{
let mut ehdr: elf::Elf64_Ehdr = Default::default();
kernel_image
@ -177,9 +177,9 @@ pub fn load_cmdline(
#[cfg(test)]
mod test {
use super::*;
use base::SharedMemory;
use std::fs::File;
use std::io::Write;
use tempfile::tempfile;
use vm_memory::{GuestAddress, GuestMemory};
const MEM_SIZE: u64 = 0x8000;
@ -233,12 +233,10 @@ mod test {
// Elf64 image that prints hello world on x86_64.
fn make_elf_bin() -> File {
let elf_bytes = include_bytes!("test_elf.bin");
let mut shm = SharedMemory::anon().expect("failed to create shared memory");
shm.set_size(elf_bytes.len() as u64)
.expect("failed to set shared memory size");
shm.write_all(elf_bytes)
let mut file = tempfile().expect("failed to create tempfile");
file.write_all(elf_bytes)
.expect("failed to write elf to shared memoy");
shm.into()
file
}
fn mutate_elf_bin(mut f: &File, offset: u64, val: u8) {

View file

@ -18,6 +18,7 @@ use std::ptr::copy_nonoverlapping;
use std::sync::Arc;
use sync::Mutex;
use base::{AsRawDescriptor, RawDescriptor};
use data_model::vec_with_array_field;
use libc::sigset_t;
@ -947,7 +948,7 @@ impl Vcpu {
let vcpu = unsafe { File::from_raw_fd(vcpu_fd) };
let run_mmap =
MemoryMapping::from_fd(&vcpu, run_mmap_size).map_err(|_| Error::new(ENOSPC))?;
MemoryMapping::from_descriptor(&vcpu, run_mmap_size).map_err(|_| Error::new(ENOSPC))?;
Ok(Vcpu { vcpu, run_mmap })
}
@ -1458,6 +1459,12 @@ impl AsRawFd for Vcpu {
}
}
impl AsRawDescriptor for Vcpu {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.vcpu.as_raw_descriptor()
}
}
/// A Vcpu that has a thread and can be run. Created by calling `to_runnable` on a `Vcpu`.
/// Implements `Deref` to a `Vcpu` so all `Vcpu` methods are usable, with the addition of the `run`
/// function to execute the guest.
@ -1619,7 +1626,7 @@ impl DerefMut for RunnableVcpu {
impl AsRawFd for RunnableVcpu {
fn as_raw_fd(&self) -> RawFd {
self.vcpu.as_raw_fd()
self.vcpu.as_raw_descriptor()
}
}

View file

@ -19,11 +19,9 @@ fn test_run() {
let mem_size = 0x10000;
let load_addr = GuestAddress(0x1000);
let guest_mem = GuestMemory::new(&[]).unwrap();
let mut mem = SharedMemory::anon().expect("failed to create shared memory");
mem.set_size(mem_size)
.expect("failed to set shared memory size");
let mmap =
MemoryMapping::from_fd(&mem, mem_size as usize).expect("failed to create memory mapping");
let mem = SharedMemory::anon(mem_size).expect("failed to create shared memory");
let mmap = MemoryMapping::from_descriptor(&mem, mem_size as usize)
.expect("failed to create memory mapping");
mmap.write_slice(&code[..], load_addr.offset() as usize)
.expect("Writing code to memory failed.");
@ -47,7 +45,7 @@ fn test_run() {
.add_memory_region(
GuestAddress(0),
Box::new(
MemoryMapping::from_fd(&mem, mem_size as usize)
MemoryMapping::from_descriptor(&mem, mem_size as usize)
.expect("failed to create memory mapping"),
),
false,

View file

@ -21,11 +21,9 @@ fn test_run() {
let mem_size = 0x2000;
let load_addr = GuestAddress(0x1000);
let guest_mem = GuestMemory::new(&[]).unwrap();
let mut mem = SharedMemory::anon().expect("failed to create shared memory");
mem.set_size(mem_size)
.expect("failed to set shared memory size");
let mmap =
MemoryMapping::from_fd(&mem, mem_size as usize).expect("failed to create memory mapping");
let mem = SharedMemory::anon(mem_size).expect("failed to create shared memory");
let mmap = MemoryMapping::from_descriptor(&mem, mem_size as usize)
.expect("failed to create memory mapping");
mmap.write_slice(&code[..], load_addr.offset() as usize)
.expect("Writing code to memory failed.");
@ -49,7 +47,7 @@ fn test_run() {
vm.add_memory_region(
GuestAddress(0),
Box::new(
MemoryMapping::from_fd(&mem, mem_size as usize)
MemoryMapping::from_descriptor(&mem, mem_size as usize)
.expect("failed to create memory mapping"),
),
false,
@ -59,17 +57,18 @@ fn test_run() {
// Give some read only memory for the test code to read from and force a vcpu exit when it reads
// from it.
let mut mem_ro = SharedMemory::anon().expect("failed to create shared memory");
mem_ro
.set_size(0x1000)
.expect("failed to set shared memory size");
let mmap_ro = MemoryMapping::from_fd(&mem_ro, 0x1000).expect("failed to create memory mapping");
let mem_ro = SharedMemory::anon(0x1000).expect("failed to create shared memory");
let mmap_ro =
MemoryMapping::from_descriptor(&mem_ro, 0x1000).expect("failed to create memory mapping");
mmap_ro
.write_obj(vcpu_regs.rax as u8, 0)
.expect("failed writing data to ro memory");
vm.add_memory_region(
GuestAddress(vcpu_sregs.es.base),
Box::new(MemoryMapping::from_fd(&mem_ro, 0x1000).expect("failed to create memory mapping")),
Box::new(
MemoryMapping::from_descriptor(&mem_ro, 0x1000)
.expect("failed to create memory mapping"),
),
true,
false,
)

View file

@ -22,7 +22,7 @@ use protobuf::Message;
use base::{
error, Error as SysError, EventFd, Killable, MemoryMapping, Result as SysResult, ScmSocket,
SharedMemory, SIGRTMIN,
SharedMemory, SharedMemoryUnix, SIGRTMIN,
};
use kvm::{dirty_log_bitmap_size, Datamatch, IoeventAddress, IrqRoute, IrqSource, PicId, Vm};
use kvm_sys::{kvm_clock_data, kvm_ioapic_state, kvm_pic_state, kvm_pit_state2};
@ -360,7 +360,7 @@ impl Process {
None => return Err(SysError::new(EOVERFLOW)),
_ => {}
}
let mem = MemoryMapping::from_fd_offset(&shm, length as usize, offset)
let mem = MemoryMapping::from_descriptor_offset(&shm, length as usize, offset)
.map_err(mmap_to_sys_err)?;
let slot =
vm.add_memory_region(GuestAddress(start), Box::new(mem), read_only, dirty_log)?;

View file

@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fs::File;
use std::mem;
use std::ops::Drop;
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::{errno_result, Result};
@ -120,6 +121,52 @@ impl AsRawDescriptor for Descriptor {
}
}
// AsRawFd for interoperability with interfaces that require it. Within crosvm,
// always use AsRawDescriptor when possible.
impl AsRawFd for Descriptor {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
macro_rules! AsRawDescriptor {
($name:ident) => {
impl AsRawDescriptor for $name {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.as_raw_fd()
}
}
};
}
macro_rules! FromRawDescriptor {
($name:ident) => {
impl FromRawDescriptor for $name {
unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
$name::from_raw_fd(descriptor)
}
}
};
}
macro_rules! IntoRawDescriptor {
($name:ident) => {
impl IntoRawDescriptor for $name {
fn into_raw_descriptor(self) -> RawDescriptor {
self.into_raw_fd()
}
}
};
}
// Implementations for File. This enables the File-type to use
// RawDescriptor, but does not mean File should be used as a generic
// descriptor container. That should go to either SafeDescriptor or another more
// relevant container type.
AsRawDescriptor!(File);
FromRawDescriptor!(File);
IntoRawDescriptor!(File);
#[test]
fn clone_equality() {
let ret = unsafe { libc::eventfd(0, 0) };

View file

@ -6,7 +6,7 @@
use std::env::{current_exe, var_os};
use std::ffi::OsString;
use std::fs::{remove_file, File};
use std::fs::remove_file;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
@ -14,8 +14,9 @@ use std::process::{Command, Stdio};
use std::thread::sleep;
use std::time::Duration;
use base::{ioctl, SharedMemory};
use base::{ioctl, AsRawDescriptor};
use rand_ish::urandom_str;
use tempfile::tempfile;
struct RemovePath(PathBuf);
impl Drop for RemovePath {
@ -134,28 +135,27 @@ fn keep_fd_on_exec<F: AsRawFd>(f: &F) {
/// Takes assembly source code and returns the resulting assembly code.
fn build_assembly(src: &str) -> Vec<u8> {
// Creates a shared memory region with the assembly source code in it.
let in_shm = SharedMemory::anon().unwrap();
let mut in_shm_file: File = in_shm.into();
keep_fd_on_exec(&in_shm_file);
in_shm_file.write_all(src.as_bytes()).unwrap();
// Creates a file with the assembly source code in it.
let mut in_file = tempfile().expect("failed to create tempfile");
keep_fd_on_exec(&in_file);
in_file.write_all(src.as_bytes()).unwrap();
// Creates a shared memory region that will hold the nasm output.
let mut out_shm_file: File = SharedMemory::anon().unwrap().into();
keep_fd_on_exec(&out_shm_file);
// Creates a file that will hold the nasm output.
let mut out_file = tempfile().expect("failed to create tempfile");
keep_fd_on_exec(&out_file);
// Runs nasm with the input and output files set to the FDs of the above shared memory regions,
// which we have preserved accross exec.
let status = Command::new("nasm")
.arg(format!("/proc/self/fd/{}", in_shm_file.as_raw_fd()))
.arg(format!("/proc/self/fd/{}", in_file.as_raw_descriptor()))
.args(&["-f", "bin", "-o"])
.arg(format!("/proc/self/fd/{}", out_shm_file.as_raw_fd()))
.arg(format!("/proc/self/fd/{}", out_file.as_raw_descriptor()))
.status()
.expect("failed to spawn assembler");
assert!(status.success());
let mut out_bytes = Vec::new();
out_shm_file.read_to_end(&mut out_bytes).unwrap();
out_file.read_to_end(&mut out_bytes).unwrap();
out_bytes
}

View file

@ -20,8 +20,8 @@ use std::sync::Arc;
use libc::{EINVAL, EIO, ENODEV};
use base::{
error, Error as SysError, EventFd, ExternalMapping, MappedRegion, MemoryMapping, MmapError,
Result,
error, AsRawDescriptor, Error as SysError, EventFd, ExternalMapping, MappedRegion,
MemoryMapping, MmapError, RawDescriptor, Result,
};
use hypervisor::{IrqRoute, IrqSource, Vm};
use msg_socket::{MsgError, MsgOnSocket, MsgReceiver, MsgResult, MsgSender, MsgSocket};
@ -37,15 +37,22 @@ pub enum MaybeOwnedFd {
/// Owned by this enum variant, and will be destructed automatically if not moved out.
Owned(File),
/// A file descriptor borrwed by this enum.
Borrowed(RawFd),
Borrowed(RawDescriptor),
}
impl AsRawDescriptor for MaybeOwnedFd {
fn as_raw_descriptor(&self) -> RawDescriptor {
match self {
MaybeOwnedFd::Owned(f) => f.as_raw_descriptor(),
MaybeOwnedFd::Borrowed(descriptor) => *descriptor,
}
}
}
// TODO(mikehoyle): Remove this in favor of just AsRawDescriptor
impl AsRawFd for MaybeOwnedFd {
fn as_raw_fd(&self) -> RawFd {
match self {
MaybeOwnedFd::Owned(f) => f.as_raw_fd(),
MaybeOwnedFd::Borrowed(fd) => *fd,
}
self.as_raw_descriptor()
}
}
@ -370,7 +377,7 @@ impl VmMemoryRequest {
offset,
gpa,
} => {
let mmap = match MemoryMapping::from_fd_offset(fd, size, offset as u64) {
let mmap = match MemoryMapping::from_descriptor_offset(fd, size, offset as u64) {
Ok(v) => v,
Err(_e) => return VmMemoryResponse::Err(SysError::new(EINVAL)),
};
@ -571,11 +578,11 @@ pub enum VmRequest {
fn register_memory(
vm: &mut impl Vm,
allocator: &mut SystemAllocator,
fd: &dyn AsRawFd,
fd: &dyn AsRawDescriptor,
size: usize,
pci_allocation: Option<(Alloc, u64)>,
) -> Result<(u64, MemSlot)> {
let mmap = match MemoryMapping::from_fd(fd, size) {
let mmap = match MemoryMapping::from_descriptor(fd, size) {
Ok(v) => v,
Err(MmapError::SystemCallFailed(e)) => return Err(e),
_ => return Err(SysError::new(EINVAL)),

View file

@ -14,8 +14,10 @@ use std::sync::Arc;
use crate::guest_address::GuestAddress;
use base::{pagesize, Error as SysError};
use base::{MappedRegion, MemoryMapping, MmapError};
use base::{MemfdSeals, SharedMemory};
use base::{
AsRawDescriptor, MappedRegion, MemfdSeals, MemoryMapping, MmapError, SharedMemory,
SharedMemoryUnix,
};
use cros_async::{
uring_mem::{self, BorrowedIoVec},
BackingMemory,
@ -33,7 +35,6 @@ pub enum Error {
MemoryRegionTooLarge(u64),
MemoryNotAligned,
MemoryCreationFailed(SysError),
MemorySetSizeFailed(SysError),
MemoryAddSealsFailed(SysError),
ShortWrite { expected: usize, completed: usize },
ShortRead { expected: usize, completed: usize },
@ -62,7 +63,6 @@ impl Display for Error {
MemoryRegionTooLarge(size) => write!(f, "memory region size {} is too large", size),
MemoryNotAligned => write!(f, "memfd regions must be page aligned"),
MemoryCreationFailed(_) => write!(f, "failed to create memfd region"),
MemorySetSizeFailed(e) => write!(f, "failed to set memfd region size: {}", e),
MemoryAddSealsFailed(e) => write!(f, "failed to set seals on memfd region: {}", e),
ShortWrite {
expected,
@ -117,7 +117,7 @@ pub struct GuestMemory {
impl AsRawFd for GuestMemory {
fn as_raw_fd(&self) -> RawFd {
self.memfd.as_raw_fd()
self.memfd.as_raw_descriptor()
}
}
@ -146,10 +146,8 @@ impl GuestMemory {
seals.set_grow_seal();
seals.set_seal_seal();
let mut memfd = SharedMemory::named("crosvm_guest").map_err(Error::MemoryCreationFailed)?;
memfd
.set_size(aligned_size)
.map_err(Error::MemorySetSizeFailed)?;
let mut memfd = SharedMemory::named("crosvm_guest", aligned_size)
.map_err(Error::MemoryCreationFailed)?;
memfd
.add_seals(seals)
.map_err(Error::MemoryAddSealsFailed)?;
@ -180,7 +178,7 @@ impl GuestMemory {
let size =
usize::try_from(range.1).map_err(|_| Error::MemoryRegionTooLarge(range.1))?;
let mapping = MemoryMapping::from_fd_offset(&memfd, size, offset)
let mapping = MemoryMapping::from_descriptor_offset(&memfd, size, offset)
.map_err(Error::MemoryMappingFailed)?;
regions.push(MemoryRegion {
mapping,
@ -545,7 +543,7 @@ impl GuestMemory {
pub fn read_to_memory(
&self,
guest_addr: GuestAddress,
src: &dyn AsRawFd,
src: &dyn AsRawDescriptor,
count: usize,
) -> Result<()> {
self.do_in_region(guest_addr, move |mapping, offset| {
@ -583,7 +581,7 @@ impl GuestMemory {
pub fn write_from_memory(
&self,
guest_addr: GuestAddress,
dst: &dyn AsRawFd,
dst: &dyn AsRawDescriptor,
count: usize,
) -> Result<()> {
self.do_in_region(guest_addr, move |mapping, offset| {
@ -853,7 +851,7 @@ mod tests {
.unwrap();
let _ = gm.with_regions::<_, ()>(|index, _, size, _, memfd_offset| {
let mmap = MemoryMapping::from_fd_offset(&gm, size, memfd_offset).unwrap();
let mmap = MemoryMapping::from_descriptor_offset(&gm, size, memfd_offset).unwrap();
if index == 0 {
assert!(mmap.read_obj::<u16>(0x0).unwrap() == 0x1337u16);

View file

@ -7,8 +7,8 @@
use std::fmt::{self, Display};
use std::io::{Read, Seek, SeekFrom};
use std::os::unix::io::AsRawFd;
use base::AsRawDescriptor;
use vm_memory::{GuestAddress, GuestMemory};
use crate::bootparam::boot_params;
@ -58,7 +58,7 @@ pub fn load_bzimage<F>(
kernel_image: &mut F,
) -> Result<(boot_params, u64)>
where
F: Read + Seek + AsRawFd,
F: Read + Seek + AsRawDescriptor,
{
let mut params: boot_params = Default::default();
kernel_image