//! Page moves, release and heap placement. use domain::{ error::KernelError, memory::frame::{FrameUse, PAGE_SIZE}, }; use platform::Platform; use types::{ address::{PhysicalAddress, VirtualAddress}, id::ProcessId, mapping::PagePerms, }; use super::{USER_HEAP_START, page_at}; use crate::Kernel; impl Kernel

{ /// Frame backing the user page at `va` in `pid`. Lets boot code fill /// pages it has just mapped for a process. pub fn user_page_frame(&self, pid: ProcessId, va: VirtualAddress) -> Option { self.user_frame(pid, va).map(|(pa, _)| pa) } /// Maps `pages` fresh zeroed pages at `va`, all or nothing. pub fn map_fresh_range( &mut self, pid: ProcessId, va: VirtualAddress, pages: usize, perms: PagePerms, ) -> Result<(), KernelError> { if !Self::in_user_window(va, pages) { return Err(KernelError::InvalidAddress); } if !self.range_is_free(pid, va, pages) { return Err(KernelError::AddressInUse); } for i in 0..pages { if let Err(e) = self.map_fresh_page(pid, page_at(va, i), perms) { for j in 0..i { self.unmap_and_free(pid, page_at(va, j)); } return Err(e); } } Ok(()) } /// Next-fit search for `pages` free heap pages from `pid`'s last request, then from the bottom. pub(crate) fn find_free_heap_range( &self, pid: ProcessId, pages: usize, ) -> Option { let bottom = USER_HEAP_START.max(P::USER_VA_START); let cursor = self .heap_cursors .get(pid.raw() as usize) .copied() .unwrap_or(0) .max(bottom); self.first_fit_from(pid, cursor, pages) .or_else(|| self.first_fit_from(pid, bottom, pages)) } /// Records where `pid`'s next heap search starts. pub(crate) fn advance_heap_cursor(&mut self, pid: ProcessId, past: VirtualAddress) { if let Some(c) = self.heap_cursors.get_mut(pid.raw() as usize) { *c = past.raw(); } } fn first_fit_from(&self, pid: ProcessId, from: usize, pages: usize) -> Option { let mut candidate = from; while Self::in_user_window(VirtualAddress::from_raw(candidate), pages) { let va = VirtualAddress::from_raw(candidate); match (0..pages).find(|&i| self.is_mapped(pid, page_at(va, i))) { None => return Some(va), Some(busy) => candidate += (busy + 1) * PAGE_SIZE, } } None } /// Validates that every page of the range is a user page `pid` owns, /// then releases them all. pub(crate) fn release_range( &mut self, pid: ProcessId, va: VirtualAddress, pages: usize, ) -> Result<(), KernelError> { if !Self::in_user_window(va, pages) { return Err(KernelError::InvalidAddress); } if !(0..pages).all(|i| self.user_frame(pid, page_at(va, i)).is_some()) { return Err(KernelError::InvalidAddress); } for i in 0..pages { self.unmap_and_free(pid, page_at(va, i)); } Ok(()) } /// Moves the page at `src` in `from` to `dst` in `to`. The frame and its /// quota charge change hands; `from` loses access. pub(crate) fn move_page( &mut self, from: ProcessId, src: VirtualAddress, to: ProcessId, dst: VirtualAddress, perms: PagePerms, ) -> Result<(), KernelError> { if !Self::in_user_window(src, 1) || !Self::in_user_window(dst, 1) { return Err(KernelError::InvalidAddress); } let (pa, _) = self .user_frame(from, src) .ok_or(KernelError::InvalidAddress)?; self.processes.get(to)?; if !self.range_is_free(to, dst, 1) { return Err(KernelError::AddressInUse); } let usage = FrameUse::user(to, dst, perms).ok_or(KernelError::InvalidAddress)?; if !self.quota_mut(to)?.charge(1) { return Err(KernelError::OutOfMemory); } if let Err(e) = self.platform_map(to, dst, pa, perms) { self.quota_mut(to)?.uncharge(1); return Err(e); } self.platform.unmap_page(from, src); self.frames.retag(pa, usage); self.quota_mut(from)?.uncharge(1); Ok(()) } }