diff --git a/src/context/memory.rs b/src/context/memory.rs index fbff1be104..c3c5423e79 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs @@ -277,6 +277,34 @@ impl AddrSpace { Ok(selected_span.base) } + pub fn r#move(dst: &mut AddrSpace, src: &mut AddrSpace, src_span: PageSpan, requested_dst_base: Option, new_flags: MapFlags) -> Result { + let nz_count = NonZeroUsize::new(src_span.count).ok_or(Error::new(EINVAL))?; + + let grant_base = { + let mut conflicts_iter = src.grants.conflicts(src_span); + + let (grant_base, grant_info) = conflicts_iter.next().ok_or(Error::new(EINVAL))?; + + if conflicts_iter.next().is_some() { + return Err(Error::new(EINVAL)); + } + if grant_info.is_pinned() { + return Err(Error::new(EBUSY)); + } + + grant_base + }; + + let grant = src.grants.remove(grant_base).expect("grant cannot disappear"); + let (before, middle, after) = grant.extract(src_span).expect("called intersect(), must succeed"); + + if let Some(before) = before { src.grants.insert(before); } + if let Some(after) = after { src.grants.insert(after); } + + let src_flusher = PageFlushAll::new(); + + dst.mmap(requested_dst_base, nz_count, new_flags, |dst_page, flags, dst_mapper, dst_flusher| middle.transfer(dst_page, flags, &mut src.table.utable, dst_mapper, src_flusher, dst_flusher)) + } } #[derive(Debug)] @@ -812,14 +840,27 @@ impl Grant { }, }) } - pub fn transfer(mut src_grant: Grant, dst_base: Page, src_mapper: &mut PageMapper, dst_mapper: &mut PageMapper, src_flusher: impl Flusher, dst_flusher: impl Flusher) -> Result { - todo!() - /* - assert!(core::mem::replace(&mut src_grant.info.mapped, false)); - let desc_opt = src_grant.info.desc_opt.take(); + /// Move a grant between two address spaces. + pub fn transfer(mut self, dst_base: Page, flags: PageFlags, src_mapper: &mut PageMapper, dst_mapper: &mut PageMapper, mut src_flusher: impl Flusher, mut dst_flusher: impl Flusher) -> Result { + assert!(!self.info.is_pinned()); - Self::copy_inner(src_grant.base, dst_base, src_grant.info.page_count, src_grant.info.flags(), desc_opt, src_mapper, dst_mapper, src_flusher, dst_flusher, src_grant.info.owned, true).map_err(Into::into) - */ + for src_page in self.span().pages() { + let dst_page = dst_base.next_by(src_page.offset_from(self.base)); + + let unmap_parents = true; + + // TODO: Validate flags? + let Some((phys, _flags, flush)) = (unsafe { src_mapper.unmap_phys(src_page.start_address(), unmap_parents) }) else { + continue; + }; + src_flusher.consume(flush); + + // TODO: Preallocate to handle OOM? + let flush = unsafe { dst_mapper.map_phys(dst_page.start_address(), phys, flags).expect("TODO: OOM") }; + dst_flusher.consume(flush); + } + + Ok(self) } pub fn remap(&mut self, mapper: &mut PageMapper, mut flusher: impl Flusher, flags: PageFlags) { diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index 99f129a949..34eb7e9b46 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -682,42 +682,20 @@ impl KernelScheme for ProcScheme { } let (requested_dst_page, page_count) = crate::syscall::validate_region(map.address, map.size)?; - let (src_page, _) = crate::syscall::validate_region(map.offset, map.size)?; + let src_span = PageSpan::validate_nonempty(VirtualAddress::new(map.offset), map.size).ok_or(Error::new(EINVAL))?; - let requested_dst_page = (map.address != 0).then_some(requested_dst_page); + let requested_dst_base = (map.address != 0).then_some(requested_dst_page); let mut src_addr_space = addrspace.write(); - let src_addr_space = &mut *src_addr_space; let mut dst_addr_space = dst_addr_space.write(); - let src_grant_span = { - let src_span = PageSpan::new(src_page, page_count); - let mut conflicts = src_addr_space.grants.conflicts(src_span); - let (first_base, first_info) = conflicts.next().ok_or(Error::new(EINVAL))?; - if conflicts.next().is_some() { - return Err(Error::new(EINVAL)); - } - - if !first_info.can_have_flags(map.flags) { - return Err(Error::new(EACCES)); - } - - PageSpan::new(first_base, first_info.page_count()).intersection(src_span) - }; - - let src_mapper = &mut src_addr_space.table.utable; - let src_page_count = NonZeroUsize::new(src_grant_span.count).ok_or(Error::new(EINVAL))?; + let src_page_count = NonZeroUsize::new(src_span.count).ok_or(Error::new(EINVAL))?; + // TODO: Validate flags let result_base = if consume { - let grant = src_addr_space.grants.remove(src_grant_span.base).expect("grant cannot disappear"); - let (before, middle, after) = grant.extract(src_grant_span).expect("called intersect(), must succeed"); - - if let Some(before) = before { src_addr_space.grants.insert(before); } - if let Some(after) = after { src_addr_space.grants.insert(after); } - - dst_addr_space.mmap(requested_dst_page, src_page_count, map.flags, |dst_page, _flags, dst_mapper, dst_flusher| Grant::transfer(middle, dst_page, src_mapper, dst_mapper, InactiveFlusher::new(), dst_flusher))? + AddrSpace::r#move(&mut *dst_addr_space, &mut *src_addr_space, src_span, requested_dst_base, map.flags)? } else { - dst_addr_space.mmap(requested_dst_page, src_page_count, map.flags, |dst_page, flags, dst_mapper, flusher| Ok(Grant::borrow(Arc::clone(addrspace), src_addr_space, src_grant_span.base, dst_page, src_grant_span.count, flags, dst_mapper, flusher, true, true, false)?))? + dst_addr_space.mmap(requested_dst_base, src_page_count, map.flags, |dst_page, flags, dst_mapper, flusher| Ok(Grant::borrow(Arc::clone(addrspace), &*src_addr_space, src_span.base, dst_page, src_span.count, flags, dst_mapper, flusher, true, true, false)?))? }; Ok(result_base.start_address().data())