//! memory set, area //! and the inactive page table use alloc::{boxed::Box, vec::Vec, string::String}; use core::fmt::{Debug, Error, Formatter}; use crate::paging::*; use super::*; use self::handler::MemoryHandler; pub mod handler; /// a continuous memory space when the same attribute /// like `vma_struct` in ucore #[derive(Debug, Clone)] pub struct MemoryArea { start_addr: VirtAddr, end_addr: VirtAddr, attr: MemoryAttr, handler: Box, name: &'static str, } unsafe impl Send for MemoryArea { } impl MemoryArea { /* ** @brief get slice of the content in the memory area ** @retval &[u8] the slice of the content in the memory area */ pub unsafe fn as_slice(&self) -> &[u8] { ::core::slice::from_raw_parts(self.start_addr as *const u8, self.end_addr - self.start_addr) } /* ** @brief get mutable slice of the content in the memory area ** @retval &mut[u8] the mutable slice of the content in the memory area */ pub unsafe fn as_slice_mut(&self) -> &mut [u8] { ::core::slice::from_raw_parts_mut(self.start_addr as *mut u8, self.end_addr - self.start_addr) } /* ** @brief test whether a virtual address is in the memory area ** @param addr: VirtAddr the virtual address to test ** @retval bool whether the virtual address is in the memory area */ pub fn contains(&self, addr: VirtAddr) -> bool { addr >= self.start_addr && addr < self.end_addr } /// Check the array is within the readable memory fn check_read_array(&self, ptr: *const S, count: usize) -> bool { ptr as usize >= self.start_addr && unsafe { ptr.add(count) as usize } <= self.end_addr } /// Check the array is within the writable memory fn check_write_array(&self, ptr: *mut S, count: usize) -> bool { !self.attr.readonly && self.check_read_array(ptr, count) } /// Check the null-end C string is within the readable memory, and is valid. /// If so, clone it to a String. /// /// Unsafe: the page table must be active. pub unsafe fn check_and_clone_cstr(&self, ptr: *const u8) -> Option { if ptr as usize >= self.end_addr { return None; } let max_len = self.end_addr - ptr as usize; (0..max_len) .find(|&i| ptr.offset(i as isize).read() == 0) .and_then(|len| core::str::from_utf8(core::slice::from_raw_parts(ptr, len)).ok()) .map(|s| String::from(s)) } /// Test whether this area is (page) overlap with area [`start_addr`, `end_addr`] pub fn is_overlap_with(&self, start_addr: VirtAddr, end_addr: VirtAddr) -> bool { let p0 = Page::of_addr(self.start_addr); let p1 = Page::of_addr(self.end_addr - 1) + 1; let p2 = Page::of_addr(start_addr); let p3 = Page::of_addr(end_addr - 1) + 1; !(p1 <= p2 || p0 >= p3) } /* ** @brief map the memory area to the physice address in a page table ** @param pt: &mut T::Active the page table to use ** @retval none */ fn map(&self, pt: &mut PageTable) { for page in Page::range_of(self.start_addr, self.end_addr) { self.handler.map(pt, page.start_address(), &self.attr); } } /* ** @brief map the memory area to the physice address in a page table eagerly ** @param pt: &mut T::Active the page table to use ** @retval none */ fn map_eager(&self, pt: &mut PageTable) { for page in Page::range_of(self.start_addr, self.end_addr) { self.handler.map_eager(pt, page.start_address(), &self.attr); } } /* ** @brief unmap the memory area from the physice address in a page table ** @param pt: &mut T::Active the page table to use ** @retval none */ fn unmap(&self, pt: &mut PageTable) { for page in Page::range_of(self.start_addr, self.end_addr) { self.handler.unmap(pt, page.start_address()); } } } /// The attributes of the memory #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] pub struct MemoryAttr { user: bool, readonly: bool, execute: bool, mmio: u8, } impl MemoryAttr { /* ** @brief set the memory attribute's user bit ** @retval MemoryAttr the memory attribute itself */ pub fn user(mut self) -> Self { self.user = true; self } /* ** @brief set the memory attribute's readonly bit ** @retval MemoryAttr the memory attribute itself */ pub fn readonly(mut self) -> Self { self.readonly = true; self } /* ** @brief unset the memory attribute's readonly bit ** @retval MemoryAttr the memory attribute itself */ pub fn writable(mut self) -> Self { self.readonly = false; self } /* ** @brief set the memory attribute's execute bit ** @retval MemoryAttr the memory attribute itself */ pub fn execute(mut self) -> Self { self.execute = true; self } /* ** @brief set the MMIO type ** @retval MemoryAttr the memory attribute itself */ pub fn mmio(mut self, value: u8) -> Self { self.mmio = value; self } /// Apply the attributes to page table entry, then update it. /// NOTE: You may need to set present manually. pub fn apply(&self, entry: &mut Entry) { entry.set_user(self.user); entry.set_writable(!self.readonly); entry.set_execute(self.execute); entry.set_mmio(self.mmio); entry.update(); } } /// set of memory space with multiple memory area with associated page table and stack space /// like `mm_struct` in ucore pub struct MemorySet { areas: Vec, page_table: T, } impl MemorySet { /* ** @brief create a memory set ** @retval MemorySet the memory set created */ pub fn new() -> Self { MemorySet { areas: Vec::new(), page_table: T::new(), } } pub fn new_bare() -> Self { MemorySet { areas: Vec::new(), page_table: T::new_bare(), } } /// Check the pointer is within the readable memory pub fn check_read_ptr(&self, ptr: *const S) -> VMResult<()> { self.check_read_array(ptr, 1) } /// Check the pointer is within the writable memory pub fn check_write_ptr(&self, ptr: *mut S) -> VMResult<()> { self.check_write_array(ptr, 1) } /// Check the array is within the readable memory pub fn check_read_array(&self, ptr: *const S, count: usize) -> VMResult<()> { self.areas.iter() .find(|area| area.check_read_array(ptr, count)) .map(|_|()).ok_or(VMError::InvalidPtr) } /// Check the array is within the writable memory pub fn check_write_array(&self, ptr: *mut S, count: usize) -> VMResult<()> { self.areas.iter() .find(|area| area.check_write_array(ptr, count)) .map(|_|()).ok_or(VMError::InvalidPtr) } /// Check the null-end C string is within the readable memory, and is valid. /// If so, clone it to a String. /// /// Unsafe: the page table must be active. pub unsafe fn check_and_clone_cstr(&self, ptr: *const u8) -> VMResult { self.areas.iter() .filter_map(|area| area.check_and_clone_cstr(ptr)) .next().ok_or(VMError::InvalidPtr) } /// Find a free area with hint address `addr_hint` and length `len`. /// Return the start address of found free area. /// Used for mmap. pub fn find_free_area(&self, addr_hint: usize, len: usize) -> VirtAddr { // brute force: // try each area's end address as the start core::iter::once(addr_hint) .chain(self.areas.iter().map(|area| area.end_addr)) .map(|addr| (addr + PAGE_SIZE - 1) & !(PAGE_SIZE - 1)) // round up a page .find(|&addr| self.test_free_area(addr, addr + len)) .expect("failed to find free area ???") } /// Test if [`start_addr`, `end_addr`) is a free area fn test_free_area(&self, start_addr: usize, end_addr: usize) -> bool { self.areas.iter() .find(|area| area.is_overlap_with(start_addr, end_addr)) .is_none() } /* ** @brief add the memory area to the memory set ** @param area: MemoryArea the memory area to add ** @retval none */ pub fn push(&mut self, start_addr: VirtAddr, end_addr: VirtAddr, attr: MemoryAttr, handler: impl MemoryHandler, name: &'static str) { assert!(start_addr <= end_addr, "invalid memory area"); assert!(self.test_free_area(start_addr, end_addr), "memory area overlap"); let area = MemoryArea { start_addr, end_addr, attr, handler: Box::new(handler), name }; self.page_table.edit(|pt| area.map(pt)); self.areas.push(area); } /* ** @brief remove the memory area from the memory set ** @param area: MemoryArea the memory area to remove ** @retval none */ pub fn pop(&mut self, start_addr: VirtAddr, end_addr: VirtAddr) { assert!(start_addr <= end_addr, "invalid memory area"); for i in 0..self.areas.len() { if self.areas[i].start_addr == start_addr && self.areas[i].end_addr == end_addr { let area = self.areas.remove(i); self.page_table.edit(|pt| area.unmap(pt)); return; } } panic!("no memory area found"); } /* ** @brief remove the memory area from the memory set and split existed ones when necessary ** @param area: MemoryArea the memory area to remove ** @retval none */ pub fn pop_with_split(&mut self, start_addr: VirtAddr, end_addr: VirtAddr) { assert!(start_addr <= end_addr, "invalid memory area"); let mut i = 0; while i < self.areas.len() { if self.areas[i].is_overlap_with(start_addr, end_addr) { if self.areas[i].start_addr >= start_addr && self.areas[i].end_addr <= end_addr { // subset let area = self.areas.remove(i); self.page_table.edit(|pt| area.unmap(pt)); i -= 1; } else if self.areas[i].start_addr >= start_addr && self.areas[i].start_addr < end_addr { // prefix let area = self.areas.remove(i); let dead_area = MemoryArea { start_addr: area.start_addr, end_addr, attr: area.attr, handler: area.handler.box_clone(), name: area.name }; self.page_table.edit(|pt| dead_area.unmap(pt)); let new_area = MemoryArea { start_addr: end_addr, end_addr: area.end_addr, attr: area.attr, handler: area.handler, name: area.name }; self.areas.insert(i, new_area); } else if self.areas[i].end_addr <= end_addr && self.areas[i].end_addr > start_addr { // postfix let area = self.areas.remove(i); let dead_area = MemoryArea { start_addr: start_addr, end_addr: area.end_addr, attr: area.attr, handler: area.handler.box_clone(), name: area.name }; self.page_table.edit(|pt| dead_area.unmap(pt)); let new_area = MemoryArea { start_addr: area.start_addr, end_addr: start_addr, attr: area.attr, handler: area.handler, name: area.name }; self.areas.insert(i, new_area); } else { // superset let area = self.areas.remove(i); let dead_area = MemoryArea { start_addr: start_addr, end_addr: end_addr, attr: area.attr, handler: area.handler.box_clone(), name: area.name }; self.page_table.edit(|pt| dead_area.unmap(pt)); let new_area_left = MemoryArea { start_addr: area.start_addr, end_addr: start_addr, attr: area.attr, handler: area.handler.box_clone(), name: area.name }; self.areas.insert(i, new_area_left); let new_area_right = MemoryArea { start_addr: end_addr, end_addr: area.end_addr, attr: area.attr, handler: area.handler, name: area.name }; self.areas.insert(i + 1, new_area_right); i += 1; } } i += 1; } } /* ** @brief get iterator of the memory area ** @retval impl Iterator ** the memory area iterator */ pub fn iter(&self) -> impl Iterator { self.areas.iter() } pub fn edit(&mut self, f: impl FnOnce(&mut T::Active)) { self.page_table.edit(f); } /* ** @brief execute function with the associated page table ** @param f: impl FnOnce() the function to be executed ** @retval none */ pub unsafe fn with(&self, f: impl FnOnce()) { self.page_table.with(f); } /* ** @brief activate the associated page table ** @retval none */ pub unsafe fn activate(&self) { self.page_table.activate(); } /* ** @brief get the token of the associated page table ** @retval usize the token of the inactive page table */ pub fn token(&self) -> usize { self.page_table.token() } /* ** @brief clear the memory set ** @retval none */ pub fn clear(&mut self) { let Self { ref mut page_table, ref mut areas, .. } = self; page_table.edit(|pt| { for area in areas.iter() { area.unmap(pt); } }); areas.clear(); } /// Get physical address of the page of given virtual `addr` pub fn translate(&mut self, addr: VirtAddr) -> Option { self.page_table.edit(|pt| { pt.get_entry(addr).and_then(|entry| { if entry.user() { Some(entry.target()) } else { None } }) }) } /* ** @brief get the mutable reference for the inactive page table ** @retval: &mut T the mutable reference of the inactive page table */ pub fn get_page_table_mut(&mut self) -> &mut T{ &mut self.page_table } pub fn handle_page_fault(&mut self, addr: VirtAddr) -> bool { let area = self.areas.iter().find(|area| area.contains(addr)); match area { Some(area) => self.page_table.edit(|pt| area.handler.handle_page_fault(pt, addr)), None => false, } } } impl Clone for MemorySet { fn clone(&self) -> Self { let mut page_table = T::new(); page_table.edit(|pt| { // without CoW, we should allocate the pages eagerly for area in self.areas.iter() { area.map_eager(pt); } }); MemorySet { areas: self.areas.clone(), page_table, } } } impl Drop for MemorySet { fn drop(&mut self) { self.clear(); } } impl Debug for MemorySet { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { f.debug_list() .entries(self.areas.iter()) .finish() } }