1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! Cross Process Mapping
//!
//! Provides mechanics for temporarily mirroring a Userland mapping in KernelLand.
//!
//! When kernel has to access memory in UserLand (for example a user has provided a buffer as an
//! argument of a syscall), it can do it in two ways:
//!
//! * Either the buffer is mapped in the same page tables that the kernel is currently using,
//!   in this case it accesses it directly, through a UserspacePtr.
//! * Either the buffer is only mapped in the page tables of another process, in which case
//!   it has to temporarily map it to KernelLand, make the modifications, and unmap it from KernelLand.
//!
//! This module covers the second case.
//!
//! The remapping is represented by a [`CrossProcessMapping`] structure. It is created from a reference
//! to the mapping being mirrored, and the KernelLand address where it will be remapped.
//! When this struct is dropped, the frames are unmap'd from KernelLand.
//!
//! A [`CrossProcessMapping`] shares the ownership of the underlying frames. As such, only refcounted
//! memory regions can be mirror-mapped. This is rarely a problem, as almost all memory is refcounted.
//!
//! There is no guarantee that the CrossProcessMapping doesn't outlive the original mapping.
//!
//! [`CrossProcessMapping`]: self::CrossProcessMapping<'a>
//! [`ProcessMemory`]: crate::paging::process_memory::ProcessMemory

use crate::mem::VirtualAddress;
use super::{PAGE_SIZE, MappingAccessRights};
use super::mapping::{Mapping, MappingFrames};
use super::kernel_memory::get_kernel_memory;
use crate::utils::{align_down, align_up};
use failure::Backtrace;
use crate::error::KernelError;

/// A struct representing a UserLand mapping mirrored in KernelSpace.
#[derive(Debug)]
pub struct CrossProcessMapping {
    /// The KernelLand address it was remapped to. Has the desired offset.
    kernel_address: VirtualAddress,
    /// Length of the region that was requested to be remapped. Note that the
    /// actual remapped region may be larger (since a Page Table mapping might
    /// need to be at least 4KB).
    len: usize,
    /// The frames this mapping covers.
    mapping: Mapping
}

#[allow(clippy::len_without_is_empty)]
impl CrossProcessMapping {
    /// Creates an `CrossProcessMapping`.
    ///
    /// Remaps a subsection of the mapping in KernelLand.
    ///
    /// The offset is a byte offset in the Mapping's underlying physical frames.
    /// If the mapping already had an offset, those are summed. For instance,
    /// a mapping with 3 frames and an offset at 0x1000, mirror_mapped with an
    /// offset of 0x1000, will result in mirror mapping from the third frame.
    ///
    /// The length is a byte length to mirror map. It's allowed to be smaller
    /// than the length of the underlying mapping.
    ///
    /// # Error
    ///
    /// * Error if the mapping is not Shared, as only refcounted mappings can be owned.
    /// * Error if `offset` + `len` > `mapping` length.
    /// * Error if `offset` + `len` would overflow.
    // todo: should be offset + (len - 1), but need to check that it wouldn't overflow in our function
    /// * Error if `len` is 0.
    ///
    /// # Panics
    ///
    /// * Panics if `mapping.phys_offset()` + `offset` overflows.
    pub fn mirror_mapping(mapping: &Mapping, offset: usize, len: usize) -> Result<CrossProcessMapping, KernelError> {
        // Ensure we have Shared frames.
        let frames = match mapping.frames() {
            MappingFrames::Shared(frames) => MappingFrames::Shared(frames.clone()),
            _ => return Err(KernelError::InvalidMemState { address: mapping.address(), ty: mapping.state().ty(), backtrace: Backtrace::new() })
        };

        // Get the full page length required for this mapping.
        let full_len = align_up((offset % PAGE_SIZE) + len, PAGE_SIZE);

        let mut kmem = get_kernel_memory();
        let kernel_map_start = kmem.find_virtual_space(full_len)?;

        // Calculate the offset from the raw PhysicalMemRegion vector.
        // NOTE: This can overflow, it's up to the caller to ensure this can't happen.
        let full_offset = mapping.phys_offset() + align_down(offset, PAGE_SIZE);

        // TODO: Use a separate MemoryType for the CrossProcessMapping
        let new_mapping = Mapping::new(kernel_map_start, frames, full_offset, full_len, mapping.state().ty(), MappingAccessRights::k_rw())?;
        unsafe {
            // safe, the frames won't be dropped, they still are tracked by the userspace mapping.
            kmem.map_frame_iterator_to(new_mapping.frames_it(), kernel_map_start, MappingAccessRights::k_rw());
        }
        Ok(CrossProcessMapping {
            kernel_address: kernel_map_start + (offset % PAGE_SIZE),
            len,
            mapping: new_mapping
        })
    }

    /// The address of the region asked to be remapped.
    pub fn addr(&self) -> VirtualAddress {
        self.kernel_address
    }

    /// The length of the region asked to be remapped.
    pub fn len(&self) -> usize {
        self.len
    }
}

impl Drop for CrossProcessMapping {
    fn drop(&mut self) {
        // don't dealloc the frames, they are tracked by the Arc.
        get_kernel_memory().unmap_no_dealloc(self.mapping.address(), self.mapping.length())
    }
}