diff --git a/kernel/src/vm/vmar/mod.rs b/kernel/src/vm/vmar/mod.rs index 73eed328..d45d9647 100644 --- a/kernel/src/vm/vmar/mod.rs +++ b/kernel/src/vm/vmar/mod.rs @@ -339,8 +339,7 @@ impl Vmar_ { } fn clear_vm_space(&self) { - let mut cursor = self.vm_space.cursor_mut(&(0..ROOT_VMAR_CAP_ADDR)).unwrap(); - cursor.unmap(ROOT_VMAR_CAP_ADDR); + self.vm_space.clear().unwrap(); } pub fn destroy(&self, range: Range) -> Result<()> { diff --git a/ostd/src/mm/page_table/cursor.rs b/ostd/src/mm/page_table/cursor.rs index 9eb108f1..a92cd451 100644 --- a/ostd/src/mm/page_table/cursor.rs +++ b/ostd/src/mm/page_table/cursor.rs @@ -76,10 +76,7 @@ use super::{ use crate::{ mm::{ kspace::should_map_as_tracked, - page::{ - meta::{MapTrackingStatus, PageTablePageMeta}, - DynPage, Page, - }, + page::{meta::MapTrackingStatus, DynPage}, Paddr, PageProperty, Vaddr, }, task::{disable_preempt, DisabledPreemptGuard}, @@ -593,8 +590,11 @@ where continue; } - // Go down if not applicable. - if cur_va % page_size::(cur_level) != 0 || cur_va + page_size::(cur_level) > end { + // Go down if not applicable or if the entry points to a child page table. + if cur_entry.is_node() + || cur_va % page_size::(cur_level) != 0 + || cur_va + page_size::(cur_level) > end + { let child = cur_entry.to_owned(); match child { Child::PageTable(pt) => { @@ -645,10 +645,7 @@ where prop, } } - Child::PageTable(node) => PageTableItem::PageTableNode { - page: Page::>::from(node).into(), - }, - Child::None => unreachable!(), + Child::PageTable(_) | Child::None => unreachable!(), }; } diff --git a/ostd/src/mm/page_table/mod.rs b/ostd/src/mm/page_table/mod.rs index df4efdd9..21707657 100644 --- a/ostd/src/mm/page_table/mod.rs +++ b/ostd/src/mm/page_table/mod.rs @@ -92,6 +92,26 @@ impl PageTable { self.root.activate(); } } + + /// Clear the page table. + /// + /// # Safety + /// + /// The caller must ensure that: + /// 1. No other cursors are accessing the page table. + /// 2. No other CPUs activates the page table. + pub(in crate::mm) unsafe fn clear(&self) { + let mut root_node = self.root.clone_shallow().lock(); + const NR_PTES_PER_NODE: usize = nr_subpage_per_huge::(); + for i in 0..NR_PTES_PER_NODE / 2 { + let root_entry = root_node.entry(i); + if !root_entry.is_none() { + let old = root_entry.replace(Child::None); + // Since no others are accessing the old child, dropping it is fine. + drop(old); + } + } + } } impl PageTable { diff --git a/ostd/src/mm/vm_space.rs b/ostd/src/mm/vm_space.rs index f4aa5e3d..b817501e 100644 --- a/ostd/src/mm/vm_space.rs +++ b/ostd/src/mm/vm_space.rs @@ -12,7 +12,9 @@ use core::{ops::Range, sync::atomic::Ordering}; use crate::{ - arch::mm::{current_page_table_paddr, PageTableEntry, PagingConsts}, + arch::mm::{ + current_page_table_paddr, tlb_flush_all_excluding_global, PageTableEntry, PagingConsts, + }, cpu::{AtomicCpuSet, CpuExceptionInfo, CpuSet, PinCurrentCpu}, cpu_local_cell, mm::{ @@ -64,6 +66,35 @@ impl VmSpace { } } + /// Clears the user space mappings in the page table. + /// + /// This method returns error if the page table is activated on any other + /// CPUs or there are any cursors alive. + pub fn clear(&self) -> core::result::Result<(), VmSpaceClearError> { + let preempt_guard = disable_preempt(); + let _guard = self + .activation_lock + .try_write() + .ok_or(VmSpaceClearError::CursorsAlive)?; + + let cpus = self.cpus.load(); + let cpu = preempt_guard.current_cpu(); + let cpus_set_is_empty = cpus.is_empty(); + let cpus_set_is_single_self = cpus.count() == 1 && cpus.contains(cpu); + + if cpus_set_is_empty || cpus_set_is_single_self { + // SAFETY: We have ensured that the page table is not activated on + // other CPUs and no cursors are alive. + unsafe { self.pt.clear() }; + if cpus_set_is_single_self { + tlb_flush_all_excluding_global(); + } + Ok(()) + } else { + Err(VmSpaceClearError::PageTableActivated(cpus)) + } + } + /// Gets an immutable cursor in the virtual address range. /// /// The cursor behaves like a lock guard, exclusively owning a sub-tree of @@ -195,6 +226,17 @@ impl Default for VmSpace { } } +/// An error that may occur when doing [`VmSpace::clear`]. +#[derive(Debug)] +pub enum VmSpaceClearError { + /// The page table is activated on other CPUs. + /// + /// The activated CPUs detected are contained in the error. + PageTableActivated(CpuSet), + /// There are still cursors alive. + CursorsAlive, +} + /// The cursor for querying over the VM space without modifying it. /// /// It exclusively owns a sub-tree of the page table, preventing others from