diff --git a/framework/aster-frame/src/vm/memory_set.rs b/framework/aster-frame/src/vm/memory_set.rs index 36323fd6..eb0eec79 100644 --- a/framework/aster-frame/src/vm/memory_set.rs +++ b/framework/aster-frame/src/vm/memory_set.rs @@ -14,7 +14,7 @@ use crate::{ Error, }; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MapArea { pub flags: PageTableFlags, pub start_va: Vaddr, @@ -28,23 +28,6 @@ pub struct MemorySet { areas: BTreeMap, } -impl Clone for MapArea { - fn clone(&self) -> Self { - let mut mapper = BTreeMap::new(); - for (&va, old) in &self.mapper { - let new = VmAllocOptions::new(1).uninit(true).alloc_single().unwrap(); - new.copy_from_frame(old); - mapper.insert(va, new.clone()); - } - Self { - start_va: self.start_va, - size: self.size, - flags: self.flags, - mapper, - } - } -} - impl MapArea { pub fn mapped_size(&self) -> usize { self.size @@ -162,17 +145,15 @@ impl MemorySet { } } - /// determine whether a Vaddr is in a mapped area + /// Determine whether a Vaddr is in a mapped area pub fn is_mapped(&self, vaddr: Vaddr) -> bool { - for (start_address, map_area) in self.areas.iter() { - if *start_address > vaddr { - break; - } - if *start_address <= vaddr && vaddr < *start_address + map_area.mapped_size() { - return true; - } - } - false + self.pt.is_mapped(vaddr) + } + + /// Return the flags of the PTE for the target virtual memory address. + /// If the PTE does not exist, return `None`. + pub fn flags(&self, vaddr: Vaddr) -> Option { + self.pt.flags(vaddr) } pub fn new() -> Self { @@ -263,6 +244,11 @@ impl MemorySet { pub fn protect(&mut self, addr: Vaddr, flags: PageTableFlags) { let va = addr; + // Temporary solution, since the `MapArea` currently only represents + // a single `VmFrame`. + if let Some(areas) = self.areas.get_mut(&va) { + areas.flags = flags; + } self.pt.protect(va, flags).unwrap(); } } diff --git a/framework/aster-frame/src/vm/page_table.rs b/framework/aster-frame/src/vm/page_table.rs index 030de390..10eec2cc 100644 --- a/framework/aster-frame/src/vm/page_table.rs +++ b/framework/aster-frame/src/vm/page_table.rs @@ -280,7 +280,7 @@ impl PageTable { paddr: Paddr, flags: T::F, ) -> Result<(), PageTableError> { - let last_entry = self.page_walk(vaddr, true).unwrap(); + let last_entry = self.do_page_walk_mut(vaddr, true).unwrap(); trace!( "Page Table: Map vaddr:{:x?}, paddr:{:x?}, flags:{:x?}", vaddr, @@ -295,22 +295,18 @@ impl PageTable { Ok(()) } - /// Find the last PTE + /// Find the last PTE and return its mutable reference. /// /// If create is set, it will create the next table until the last PTE. - /// If not, it will return None if it is not reach the last PTE. - /// - fn page_walk(&mut self, vaddr: Vaddr, create: bool) -> Option<&mut T> { - let mut count = self.config.address_width as usize; - debug_assert!(size_of::() * (T::page_index(vaddr, count) + 1) <= PAGE_SIZE); + /// If not, it will return `None` if it cannot reach the last PTE. + fn do_page_walk_mut(&mut self, vaddr: Vaddr, create: bool) -> Option<&mut T> { + let mut level = self.config.address_width as usize; // Safety: The offset does not exceed the value of PAGE_SIZE. // It only change the memory controlled by page table. - let mut current: &mut T = unsafe { - &mut *(paddr_to_vaddr(self.root_paddr + size_of::() * T::page_index(vaddr, count)) - as *mut T) - }; + let mut current: &mut T = + unsafe { &mut *(calculate_pte_vaddr::(self.root_paddr, vaddr, level) as *mut T) }; - while count > 1 { + while level > 1 { if !current.flags().is_present() { if !create { return None; @@ -329,19 +325,44 @@ impl PageTable { if current.flags().is_huge() { break; } - count -= 1; - debug_assert!(size_of::() * (T::page_index(vaddr, count) + 1) <= PAGE_SIZE); + level -= 1; // Safety: The offset does not exceed the value of PAGE_SIZE. // It only change the memory controlled by page table. current = unsafe { - &mut *(paddr_to_vaddr( - current.paddr() + size_of::() * T::page_index(vaddr, count), - ) as *mut T) + &mut *(calculate_pte_vaddr::(current.paddr(), vaddr, level) as *mut T) }; } Some(current) } + /// Find the last PTE and return its immutable reference. + /// + /// This function will return `None` if it cannot reach the last PTE. + /// Note that finding an entry does not mean the corresponding virtual memory address is mapped + /// since the entry may be empty. + fn do_page_walk(&self, vaddr: Vaddr) -> Option<&T> { + let mut level = self.config.address_width as usize; + // Safety: The offset does not exceed the value of PAGE_SIZE. + // It only change the memory controlled by page table. + let mut current: &T = + unsafe { &*(calculate_pte_vaddr::(self.root_paddr, vaddr, level) as *const T) }; + + while level > 1 { + if !current.flags().is_present() { + return None; + } + if current.flags().is_huge() { + break; + } + level -= 1; + // Safety: The offset does not exceed the value of PAGE_SIZE. + // It only change the memory controlled by page table. + current = + unsafe { &*(calculate_pte_vaddr::(current.paddr(), vaddr, level) as *const T) }; + } + Some(current) + } + /// Unmap `vaddr`. /// /// # Safety @@ -349,9 +370,11 @@ impl PageTable { /// This function allows arbitrary modifications to the page table. /// Incorrect modifications may cause the kernel to crash (e.g., unmap the linear mapping.). unsafe fn do_unmap(&mut self, vaddr: Vaddr) -> Result<(), PageTableError> { - let last_entry = self.page_walk(vaddr, false).unwrap(); + let last_entry = self + .do_page_walk_mut(vaddr, false) + .ok_or(PageTableError::InvalidModification)?; trace!("Page Table: Unmap vaddr:{:x?}", vaddr); - if !last_entry.is_used() && !last_entry.flags().is_present() { + if !last_entry.is_used() || !last_entry.flags().is_present() { return Err(PageTableError::InvalidModification); } last_entry.clear(); @@ -368,7 +391,9 @@ impl PageTable { /// Incorrect modifications may cause the kernel to crash /// (e.g., make the linear mapping visible to the user mode applications.). unsafe fn do_protect(&mut self, vaddr: Vaddr, new_flags: T::F) -> Result { - let last_entry = self.page_walk(vaddr, false).unwrap(); + let last_entry = self + .do_page_walk_mut(vaddr, false) + .ok_or(PageTableError::InvalidModification)?; let old_flags = last_entry.flags(); trace!( "Page Table: Protect vaddr:{:x?}, flags:{:x?}", @@ -402,14 +427,22 @@ impl PageTable { } } - pub fn flags(&mut self, vaddr: Vaddr) -> Option { - let last_entry = self.page_walk(vaddr, false)?; - Some(last_entry.flags()) + /// Return the flags of the PTE for the target virtual memory address. + /// If the PTE does not exist, return `None`. + pub fn flags(&self, vaddr: Vaddr) -> Option { + self.do_page_walk(vaddr).map(|entry| entry.flags()) } + /// Return the root physical address of current `PageTable`. pub fn root_paddr(&self) -> Paddr { self.root_paddr } + + /// Determine whether the target virtual memory address is mapped. + pub fn is_mapped(&self, vaddr: Vaddr) -> bool { + self.do_page_walk(vaddr) + .is_some_and(|last_entry| last_entry.is_used() && last_entry.flags().is_present()) + } } /// Read `NR_ENTRIES_PER_PAGE` of PageTableEntry from an address @@ -428,14 +461,23 @@ pub unsafe fn table_of<'a, T: PageTableEntryTrait>(pa: Paddr) -> Option<&'a mut /// translate a virtual address to physical address which cannot use offset to get physical address pub fn vaddr_to_paddr(vaddr: Vaddr) -> Option { - let mut page_table = KERNEL_PAGE_TABLE.get().unwrap().lock(); + let page_table = KERNEL_PAGE_TABLE.get().unwrap().lock(); // Although we bypass the unsafe APIs provided by KernelMode, the purpose here is // only to obtain the corresponding physical address according to the mapping. - let last_entry = page_table.page_walk(vaddr, false)?; + let last_entry = page_table.do_page_walk(vaddr)?; // FIXME: Support huge page Some(last_entry.paddr() + (vaddr & (PAGE_SIZE - 1))) } +fn calculate_pte_vaddr( + root_pa: Paddr, + target_va: Vaddr, + level: usize, +) -> Vaddr { + debug_assert!(size_of::() * (T::page_index(target_va, level) + 1) <= PAGE_SIZE); + paddr_to_vaddr(root_pa + size_of::() * T::page_index(target_va, level)) +} + pub fn init() { KERNEL_PAGE_TABLE.call_once(|| { // Safety: The `KERENL_PAGE_TABLE` is the only page table that is used to modify the initialize diff --git a/framework/aster-frame/src/vm/space.rs b/framework/aster-frame/src/vm/space.rs index 1211a6ae..9727bfad 100644 --- a/framework/aster-frame/src/vm/space.rs +++ b/framework/aster-frame/src/vm/space.rs @@ -31,6 +31,7 @@ impl VmSpace { memory_set: Arc::new(Mutex::new(MemorySet::new())), } } + /// Activate the page table, load root physical address to cr3 #[allow(clippy::missing_safety_doc)] pub unsafe fn activate(&self) { @@ -80,12 +81,26 @@ impl VmSpace { Ok(base_addr) } - /// determine whether a vaddr is already mapped + /// Determine whether a `vaddr` is already mapped. pub fn is_mapped(&self, vaddr: Vaddr) -> bool { let memory_set = self.memory_set.lock(); memory_set.is_mapped(vaddr) } + /// Determine whether the target `vaddr` is writable based on the page table. + pub fn is_writable(&self, vaddr: Vaddr) -> bool { + let memory_set = self.memory_set.lock(); + let flags = memory_set.flags(vaddr); + flags.is_some_and(|flags| flags.contains(PageTableFlags::WRITABLE)) + } + + /// Determine whether the target `vaddr` is executable based on the page table. + pub fn is_executable(&self, vaddr: Vaddr) -> bool { + let memory_set = self.memory_set.lock(); + let flags = memory_set.flags(vaddr); + flags.is_some_and(|flags| !flags.contains(PageTableFlags::NO_EXECUTE)) + } + /// Unmaps the physical memory pages within the VM address range. /// /// The range is allowed to contain gaps, where no physical memory pages @@ -128,6 +143,16 @@ impl VmSpace { } Ok(()) } + + /// Deep-copy the current `VmSpace`. + /// + /// The generated new `VmSpace` possesses a `MemorySet` independent from the + /// original `VmSpace`, with initial contents identical to the original. + pub fn deep_copy(&self) -> Self { + Self { + memory_set: Arc::new(Mutex::new(self.memory_set.lock().clone())), + } + } } impl Default for VmSpace {