blob: d9fa56ae8245c2b988dd349ee7b064d42be2f15a [file] [log] [blame]
// 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::{
descriptor::{AsRawDescriptor, SafeDescriptor},
platform::{MappedRegion, MemoryMapping as SysUtilMmap, MmapError},
Protection, SharedMemory,
};
use data_model::{volatile_memory::*, DataInit};
use std::fs::File;
pub type Result<T> = std::result::Result<T, MmapError>;
/// See [MemoryMapping](crate::platform::MemoryMapping) for struct- and method-level
/// documentation.
#[derive(Debug)]
pub struct MemoryMapping {
pub(crate) mapping: SysUtilMmap,
// File backed mappings on Windows need to keep the underlying file open while the mapping is
// open.
// This will be a None in non-windows case. The variable will not be read so the '^_'.
//
// TODO(b:230902713) There was a concern about relying on the kernel's refcounting to keep the
// file object's locks (e.g. exclusive read/write) in place. We need to revisit/validate that
// concern.
pub(crate) _file_descriptor: Option<SafeDescriptor>,
}
impl MemoryMapping {
pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> {
self.mapping.write_slice(buf, offset)
}
pub fn read_slice(&self, buf: &mut [u8], offset: usize) -> Result<usize> {
self.mapping.read_slice(buf, offset)
}
pub fn write_obj<T: DataInit>(&self, val: T, offset: usize) -> Result<()> {
self.mapping.write_obj(val, offset)
}
pub fn read_obj<T: DataInit>(&self, offset: usize) -> Result<T> {
self.mapping.read_obj(offset)
}
pub fn msync(&self) -> Result<()> {
self.mapping.msync()
}
}
pub struct MemoryMappingBuilder<'a> {
pub(crate) descriptor: Option<&'a dyn AsRawDescriptor>,
pub(crate) is_file_descriptor: bool,
pub(crate) size: usize,
pub(crate) offset: Option<u64>,
pub(crate) protection: Option<Protection>,
pub(crate) populate: bool,
}
/// Builds a MemoryMapping object from the specified arguments.
impl<'a> MemoryMappingBuilder<'a> {
/// Creates a new builder specifying size of the memory region in bytes.
pub fn new(size: usize) -> MemoryMappingBuilder<'a> {
MemoryMappingBuilder {
descriptor: None,
size,
is_file_descriptor: false,
offset: None,
protection: None,
populate: false,
}
}
/// Build the memory mapping given the specified File to mapped memory
///
/// Default: Create a new memory mapping.
///
/// Note: this is a forward looking interface to accomodate platforms that
/// require special handling for file backed mappings.
#[allow(clippy::wrong_self_convention, unused_mut)]
pub fn from_file(mut self, file: &'a File) -> MemoryMappingBuilder {
// On Windows, files require special handling (next day shipping if possible).
self.is_file_descriptor = true;
self.descriptor = Some(file as &dyn AsRawDescriptor);
self
}
/// Build the memory mapping given the specified SharedMemory to mapped memory
///
/// Default: Create a new memory mapping.
pub fn from_shared_memory(mut self, shm: &'a SharedMemory) -> MemoryMappingBuilder {
self.descriptor = Some(shm as &dyn AsRawDescriptor);
self
}
/// Offset in bytes from the beginning of the mapping to start the mmap.
///
/// Default: No offset
pub fn offset(mut self, offset: u64) -> MemoryMappingBuilder<'a> {
self.offset = Some(offset);
self
}
/// Protection (e.g. readable/writable) of the memory region.
///
/// Default: Read/write
pub fn protection(mut self, protection: Protection) -> MemoryMappingBuilder<'a> {
self.protection = Some(protection);
self
}
/// Build a MemoryMapping from the provided options at a fixed address. Note this
/// is a separate function from build in order to isolate unsafe behavior.
///
/// # Safety
///
/// Function should not be called before the caller unmaps any mmap'd regions already
/// present at `(addr..addr+size)`. If another MemoryMapping object holds the same
/// address space, the destructors of those objects will conflict and the space could
/// be unmapped while still in use.
///
/// WARNING: On windows, this is not compatible with from_file.
/// TODO(b:230901659): Find a better way to enforce this warning in code.
pub unsafe fn build_fixed(self, addr: *mut u8) -> Result<MemoryMapping> {
if self.populate {
// Population not supported for fixed mapping.
return Err(MmapError::InvalidArgument);
}
match self.descriptor {
None => MemoryMappingBuilder::wrap(
SysUtilMmap::new_protection_fixed(
addr,
self.size,
self.protection.unwrap_or_else(Protection::read_write),
)?,
None,
),
Some(descriptor) => MemoryMappingBuilder::wrap(
SysUtilMmap::from_descriptor_offset_protection_fixed(
addr,
descriptor,
self.size,
self.offset.unwrap_or(0),
self.protection.unwrap_or_else(Protection::read_write),
)?,
None,
),
}
}
}
impl VolatileMemory for MemoryMapping {
fn get_slice(&self, offset: usize, count: usize) -> VolatileMemoryResult<VolatileSlice> {
self.mapping.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.mapping.as_ptr()
}
fn size(&self) -> usize {
self.mapping.size()
}
}