Add more getter methods for VmSpace

This commit is contained in:
Chen Chengjun 2024-04-08 13:30:09 +08:00 committed by Tate, Hongliang Tian
parent acf956fa03
commit 233e1fac98
3 changed files with 107 additions and 54 deletions

View File

@ -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<Vaddr, MapArea>,
}
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<PageTableFlags> {
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();
}
}

View File

@ -280,7 +280,7 @@ impl<T: PageTableEntryTrait, M> PageTable<T, M> {
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<T: PageTableEntryTrait, M> PageTable<T, M> {
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>() * (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>() * T::page_index(vaddr, count))
as *mut T)
};
let mut current: &mut T =
unsafe { &mut *(calculate_pte_vaddr::<T>(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<T: PageTableEntryTrait, M> PageTable<T, M> {
if current.flags().is_huge() {
break;
}
count -= 1;
debug_assert!(size_of::<T>() * (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>() * T::page_index(vaddr, count),
) as *mut T)
&mut *(calculate_pte_vaddr::<T>(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::<T>(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::<T>(current.paddr(), vaddr, level) as *const T) };
}
Some(current)
}
/// Unmap `vaddr`.
///
/// # Safety
@ -349,9 +370,11 @@ impl<T: PageTableEntryTrait, M> PageTable<T, M> {
/// 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<T: PageTableEntryTrait, M> PageTable<T, M> {
/// 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<T::F, PageTableError> {
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<T: PageTableEntryTrait, M> PageTable<T, M> {
}
}
pub fn flags(&mut self, vaddr: Vaddr) -> Option<T::F> {
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<T::F> {
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<Paddr> {
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<T: PageTableEntryTrait>(
root_pa: Paddr,
target_va: Vaddr,
level: usize,
) -> Vaddr {
debug_assert!(size_of::<T>() * (T::page_index(target_va, level) + 1) <= PAGE_SIZE);
paddr_to_vaddr(root_pa + size_of::<T>() * 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

View File

@ -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 {