diff --git a/src/context/memory.rs b/src/context/memory.rs index 1e6127a03b8ded6a4767b200bd72019661b69080..24dd53b0ef2bbf4cdb01e160c203a18e67403211 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs @@ -113,6 +113,7 @@ impl AddrSpace { false, fmap.as_ref().map(Arc::clone), )?, + Provider::FmapBorrowed { ref fmap } => Grant::borrow_fmap(PageSpan::new(grant_base, grant_info.page_count), grant_info.flags(), Arc::clone(fmap)), }; new_guard.grants.insert(new_grant); @@ -559,7 +560,7 @@ pub struct GrantInfo { #[derive(Debug)] pub struct FmapCtxt { - file_ref: GrantFileRef, + pub file_ref: GrantFileRef, } #[derive(Debug)] @@ -576,6 +577,8 @@ pub enum Provider { /// /// All grants in the specified range must be of type Allocated. External { address_space: Arc<RwLock<AddrSpace>>, src_base: Page, fmap: Option<Arc<FmapCtxt>> }, + + FmapBorrowed { fmap: Arc<FmapCtxt> }, } #[derive(Debug)] @@ -654,6 +657,18 @@ impl Grant { }) } + pub fn borrow_fmap(span: PageSpan, flags: PageFlags<RmmA>, fmap: Arc<FmapCtxt>) -> Self { + Self { + base: span.base, + info: GrantInfo { + page_count: span.count, + mapped: true, + flags, + provider: Provider::FmapBorrowed { fmap } + } + } + } + // TODO: Do not return Vec, return an iterator perhaps? Referencing the source address space? /// Borrow all pages in the range `[src_base, src_base+page_count)` from `src_address_space`, @@ -717,6 +732,7 @@ impl Grant { base: src_phys_base.next_by(offset_from_src_base), }, Provider::External { ref address_space, src_base, ref fmap } => Provider::External { address_space: Arc::clone(address_space), src_base, fmap: fmap.as_ref().map(Arc::clone) }, + Provider::FmapBorrowed { ref fmap } => Provider::FmapBorrowed { fmap: Arc::clone(fmap) } } }, }); @@ -822,6 +838,7 @@ impl Grant { Provider::Allocated { .. } => Some(true), Provider::External { .. } => Some(false), Provider::PhysBorrowed { .. } => None, + Provider::FmapBorrowed { .. } => Some(false), }; if let Some(is_cow) = is_cow_opt { @@ -878,13 +895,14 @@ impl Grant { }, Provider::Allocated { ref fmap } => Provider::Allocated { fmap: fmap.as_ref().map(Arc::clone) }, Provider::PhysBorrowed { ref base } => Provider::PhysBorrowed { base: base.clone() }, + Provider::FmapBorrowed { ref fmap } => Provider::FmapBorrowed { fmap: Arc::clone(fmap) } } }, }); match self.info.provider { Provider::PhysBorrowed { ref mut base } => *base = base.next_by(before_grant.as_ref().map_or(0, |g| g.info.page_count)), - Provider::Allocated { .. } | Provider::External { .. } => (), + Provider::Allocated { .. } | Provider::External { .. } | Provider::FmapBorrowed { .. } => (), } @@ -903,6 +921,7 @@ impl Grant { }, Provider::PhysBorrowed { base } => Provider::PhysBorrowed { base: base.next_by(this_span.count) }, + Provider::FmapBorrowed { ref fmap } => Provider::FmapBorrowed { fmap: Arc::clone(fmap) } } }, }); @@ -1212,6 +1231,7 @@ pub fn try_correcting_page_tables(faulting_page: Page, access: AccessMode) -> Re map_zeroed(&mut guard.table.utable, src_page, grant_flags, access == AccessMode::Write)? } } + Provider::FmapBorrowed { ref fmap } => todo!(), }; if super::context_id().into() == 3 && debug { @@ -1227,3 +1247,9 @@ pub fn try_correcting_page_tables(faulting_page: Page, access: AccessMode) -> Re Ok(()) } + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum MmapMode { + Cow, + Shared, +} diff --git a/src/scheme/user.rs b/src/scheme/user.rs index 8dde12a5be839542c070fe0d6f528f17facd712b..83247f42cf6021bbdc50c5722707956ef07c3b3c 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -10,14 +10,14 @@ use spin::{Mutex, RwLock}; use crate::context::{self, Context, BorrowedHtBuf}; use crate::context::file::FileDescription; -use crate::context::memory::{AddrSpace, DANGLING, Grant, GrantFileRef, PageSpan}; +use crate::context::memory::{AddrSpace, DANGLING, Grant, GrantFileRef, PageSpan, MmapMode, page_flags, FmapCtxt}; use crate::event; use crate::paging::{PAGE_SIZE, Page, VirtualAddress}; use crate::scheme::{AtomicSchemeId, SchemeId}; use crate::sync::{WaitQueue, WaitMap}; use crate::syscall::data::{Map, Packet}; -use crate::syscall::{error::*, validate_region}; -use crate::syscall::flag::{EventFlags, EVENT_READ, O_NONBLOCK, PROT_READ, PROT_WRITE}; +use crate::syscall::error::*; +use crate::syscall::flag::{EventFlags, EVENT_READ, O_NONBLOCK, PROT_READ, PROT_WRITE, MapFlags}; use crate::syscall::number::*; use crate::syscall::scheme::Scheme; use crate::syscall::usercopy::{UserSlice, UserSliceWo, UserSliceRo}; @@ -376,21 +376,34 @@ impl UserInner { Ok(0) } - fn fmap_inner(&self, file: usize, map: &Map) -> Result<usize> { + fn fmap_inner(&self, dst_addr_space: Arc<RwLock<AddrSpace>>, file: usize, map: &Map) -> Result<usize> { let aligned_size = map.size.next_multiple_of(PAGE_SIZE); if aligned_size != map.size { log::warn!("fmap passed length {:#0x} instead of {:#0x}", map.size, aligned_size); } - let requested_dst_span_opt = (map.address != 0) - .then(|| PageSpan::validate_nonempty(VirtualAddress::new(map.address), aligned_size).ok_or(Error::new(EINVAL))) - .transpose()?; + if aligned_size == 0 { + return Err(Error::new(EINVAL)); + } + + if map.address % PAGE_SIZE != 0 { + return Err(Error::new(EINVAL)) + }; + let dst_base = (map.address != 0).then_some(Page::containing_address(VirtualAddress::new(map.address))); if map.offset % PAGE_SIZE != 0 { return Err(Error::new(EINVAL)); } - let (pid, uid, gid, context_weak, desc) = { + let mode = if map.flags.contains(MapFlags::MAP_PRIVATE) { + MmapMode::Cow + } else if map.flags.contains(MapFlags::MAP_SHARED) { + MmapMode::Shared + } else { + return Err(Error::new(EINVAL)); + }; + + let (pid, desc) = { let context_lock = context::current()?; let context = context_lock.read(); // TODO: Faster, cleaner mechanism to get descriptor @@ -407,31 +420,49 @@ impl UserInner { } } let desc = desc_res?; - (context.id, context.euid, context.egid, Arc::downgrade(&context_lock), desc) + (context.id, desc.description) }; - let id = self.next_id(); + let page_count = aligned_size / PAGE_SIZE; - let result = self.call_extended_inner(Packet { - id, + let response = self.call_extended_inner(Packet { + id: self.next_id(), pid: pid.into(), a: KSMSG_MMAP_PREP, b: file, c: map.offset, - d: map.size / PAGE_SIZE, + d: page_count, // The uid and gid can be obtained by the proc scheme anyway, if the pid is provided. uid: map.flags.bits() as u32, gid: (map.flags.bits() >> 32) as u32, - }); + })?; - result.and_then(|response| match response { - Response::Regular(code) => Error::demux(code), + let _ = match response { + Response::Regular(code) => Error::demux(code)?, Response::Fd(_) => { log::debug!("Scheme incorrectly returned an fd for fmap."); - Err(Error::new(EIO)) + return Err(Error::new(EIO)); } - }) + }; + + let dst_base = match mode { + MmapMode::Cow => todo!("mmap CoW"), + MmapMode::Shared => { + let ctxt = Arc::new(FmapCtxt { + file_ref: GrantFileRef { + description: desc, + base_offset: map.offset, + }, + }); + let page_count_nz = NonZeroUsize::new(page_count).expect("already validated map.size != 0"); + dst_addr_space.write().mmap(dst_base, page_count_nz, map.flags, |dst_base, flags, _mapper, _flusher| { + Ok(Grant::borrow_fmap(PageSpan::new(dst_base, page_count), page_flags(map.flags), ctxt)) + })? + } + }; + + Ok(dst_base.start_address().data()) } } pub struct CaptureGuard<const READ: bool, const WRITE: bool> { @@ -579,7 +610,7 @@ impl Scheme for UserScheme { fn fmap(&self, file: usize, map: &Map) -> Result<usize> { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; - inner.fmap_inner(file, map) + inner.fmap_inner(AddrSpace::current()?, file, map) } fn funmap(&self, grant_address: usize, size: usize) -> Result<usize> { @@ -711,6 +742,11 @@ impl KernelScheme for UserScheme { address.release()?; result } + fn kfmap(&self, file: usize, addr_space: &Arc<RwLock<AddrSpace>>, map: &Map, _consume: bool) -> Result<usize> { + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + + inner.fmap_inner(Arc::clone(addr_space), file, map) + } } #[derive(PartialEq)]