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()) } }