//! Memory management structures use alloc::{boxed::Box, vec::Vec}; use core::fmt::{Debug, Error, Formatter}; use core::mem::size_of; use crate::paging::*; use super::*; use self::handler::MemoryHandler; pub mod handler; /// A continuous memory space when the same attribute #[derive(Debug, Clone)] pub struct MemoryArea { start_addr: VirtAddr, end_addr: VirtAddr, attr: MemoryAttr, handler: Box, name: &'static str, } impl MemoryArea { /// Test whether a 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. /// Return the size of space covered in the area. fn check_read_array(&self, ptr: *const S, count: usize) -> usize { // page align let min_bound = (ptr as usize).max(Page::of_addr(self.start_addr).start_address()); let max_bound = unsafe { ptr.add(count) as usize } .min(Page::of_addr(self.end_addr + PAGE_SIZE - 1).start_address()); if max_bound >= min_bound { max_bound - min_bound } else { 0 } } /// Check the array is within the writable memory. /// Return the size of space covered in the area. fn check_write_array(&self, ptr: *mut S, count: usize) -> usize { if self.attr.readonly { 0 } else { self.check_read_array(ptr, count) } } /// 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) } /// Map all pages in the area to page table `pt` fn map(&self, pt: &mut dyn PageTable) { for page in Page::range_of(self.start_addr, self.end_addr) { self.handler.map(pt, page.start_address(), &self.attr); } } /// Unmap all pages in the area from page table `pt` fn unmap(&self, pt: &mut dyn 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 { pub fn user(mut self) -> Self { self.user = true; self } pub fn readonly(mut self) -> Self { self.readonly = true; self } pub fn writable(mut self) -> Self { self.readonly = false; self } pub fn execute(mut self) -> Self { self.execute = true; self } 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 dyn Entry) { entry.set_user(self.user); entry.set_writable(!self.readonly); entry.set_execute(self.execute); entry.set_mmio(self.mmio); entry.update(); } } /// A set of memory space with multiple memory areas with associated page table /// NOTE: Don't remove align(64), or you will fail to run MIPS. /// Temporary solution for rv64 #[cfg_attr(not(target_arch = "riscv64"), repr(align(64)))] pub struct MemorySet { areas: Vec, page_table: T, } impl MemorySet { /// Create a new `MemorySet` pub fn new() -> Self { MemorySet { areas: Vec::new(), page_table: T::new(), } } /// Create a new `MemorySet` for kernel remap pub fn new_bare() -> Self { MemorySet { areas: Vec::new(), page_table: T::new_bare(), } } /// Check the pointer is within the readable memory pub unsafe fn check_read_ptr(&self, ptr: *const S) -> VMResult<&'static S> { self.check_read_array(ptr, 1).map(|s| &s[0]) } /// Check the pointer is within the writable memory pub unsafe fn check_write_ptr(&self, ptr: *mut S) -> VMResult<&'static mut S> { self.check_write_array(ptr, 1).map(|s| &mut s[0]) } /// Check the array is within the readable memory pub unsafe fn check_read_array( &self, ptr: *const S, count: usize, ) -> VMResult<&'static [S]> { let mut valid_size = 0; for area in self.areas.iter() { valid_size += area.check_read_array(ptr, count); if valid_size == size_of::() * count { return Ok(core::slice::from_raw_parts(ptr, count)); } } Err(VMError::InvalidPtr) } /// Check the array is within the writable memory pub unsafe fn check_write_array( &self, ptr: *mut S, count: usize, ) -> VMResult<&'static mut [S]> { let mut valid_size = 0; for area in self.areas.iter() { valid_size += area.check_write_array(ptr, count); if valid_size == size_of::() * count { return Ok(core::slice::from_raw_parts_mut(ptr, count)); } } Err(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() } /// Add an area to this set 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, }; area.map(&mut self.page_table); // keep order by start address let idx = self .areas .iter() .enumerate() .find(|(_, other)| start_addr < other.start_addr) .map(|(i, _)| i) .unwrap_or(self.areas.len()); self.areas.insert(idx, area); } /// Remove the area `[start_addr, end_addr)` from `MemorySet` 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); area.unmap(&mut self.page_table); return; } } panic!("no memory area found"); } /// Remove the area `[start_addr, end_addr)` from `MemorySet` /// and split existed ones when necessary. 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); area.unmap(&mut self.page_table); 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, }; dead_area.unmap(&mut self.page_table); 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, end_addr: area.end_addr, attr: area.attr, handler: area.handler.box_clone(), name: area.name, }; dead_area.unmap(&mut self.page_table); 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, end_addr, attr: area.attr, handler: area.handler.box_clone(), name: area.name, }; dead_area.unmap(&mut self.page_table); 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; } } /// Get iterator of areas pub fn iter(&self) -> impl Iterator { self.areas.iter() } /// Execute function `f` with the associated page table pub unsafe fn with(&self, f: impl FnOnce()) { self.page_table.with(f); } /// Activate the associated page table pub unsafe fn activate(&self) { self.page_table.activate(); } /// Get the token of the associated page table pub fn token(&self) -> usize { self.page_table.token() } /// Clear and unmap all areas pub fn clear(&mut self) { let Self { ref mut page_table, ref mut areas, .. } = self; for area in areas.iter() { area.unmap(page_table); } areas.clear(); } /// Get physical address of the page of given virtual `addr` pub fn translate(&mut self, addr: VirtAddr) -> Option { self.page_table.get_entry(addr).and_then(|entry| { if entry.user() { Some(entry.target()) } else { None } }) } /// Get the reference of inner 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) => area.handler.handle_page_fault(&mut self.page_table, addr), None => false, } } pub fn clone(&mut self) -> Self { let mut new_page_table = T::new(); let Self { ref mut page_table, ref areas, .. } = self; for area in areas.iter() { for page in Page::range_of(area.start_addr, area.end_addr) { area.handler.clone_map( &mut new_page_table, page_table, page.start_address(), &area.attr, ); } } MemorySet { areas: areas.clone(), page_table: new_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() } }