mod mapper;
mod visitor;
extern crate alloc;
use crate::PageManager;
use alloc::vec::Vec;
use core::{fmt, ops::Range, ptr::NonNull};
use mapper::Mapper;
use page_table::{PageTable, PageTableFormatter, Pos, VAddr, VmFlags, VmMeta, PPN, VPN};
use visitor::Visitor;
pub struct AddressSpace<Meta: VmMeta, M: PageManager<Meta>> {
pub areas: Vec<Range<VPN<Meta>>>,
page_manager: M,
}
impl<Meta: VmMeta, M: PageManager<Meta>> AddressSpace<Meta, M> {
#[inline]
pub fn new() -> Self {
Self {
areas: Vec::new(),
page_manager: M::new_root(),
}
}
#[inline]
pub fn root_ppn(&self) -> PPN<Meta> {
self.page_manager.root_ppn()
}
#[inline]
pub fn root(&self) -> PageTable<Meta> {
unsafe { PageTable::from_root(self.page_manager.root_ptr()) }
}
pub fn map_extern(&mut self, range: Range<VPN<Meta>>, pbase: PPN<Meta>, flags: VmFlags<Meta>) {
self.areas.push(range.start..range.end);
let count = range.end.val() - range.start.val();
let mut root = self.root();
let mut mapper = Mapper::new(self, pbase..pbase + count, flags);
root.walk_mut(Pos::new(range.start, 0), &mut mapper);
if !mapper.ans() {
todo!()
}
}
pub fn map(
&mut self,
range: Range<VPN<Meta>>,
data: &[u8],
offset: usize,
mut flags: VmFlags<Meta>,
) {
let count = range.end.val() - range.start.val();
let size = count << Meta::PAGE_BITS;
assert!(size >= data.len() + offset);
let page = self.page_manager.allocate(count, &mut flags);
unsafe {
use core::slice::from_raw_parts_mut as slice;
let mut ptr = page.as_ptr();
slice(ptr, offset).fill(0);
ptr = ptr.add(offset);
slice(ptr, data.len()).copy_from_slice(data);
ptr = ptr.add(data.len());
slice(ptr, page.as_ptr().add(size).offset_from(ptr) as _).fill(0);
}
self.map_extern(range, self.page_manager.v_to_p(page), flags)
}
pub fn unmap(&mut self, range: Range<VPN<Meta>>) {
let mut new_areas = Vec::new();
for area in self.areas.drain(..) {
if area.end <= range.start || area.start >= range.end {
new_areas.push(area);
} else {
if area.start < range.start {
new_areas.push(area.start..range.start);
}
if area.end > range.end {
new_areas.push(range.end..area.end);
}
}
}
self.areas = new_areas;
let mut vpn = range.start;
while vpn < range.end {
if let Some(pte_ptr) = self.find_pte_mut(vpn) {
unsafe {
core::ptr::write_bytes(
pte_ptr as *mut u8,
0,
core::mem::size_of::<page_table::Pte<Meta>>(),
)
};
}
vpn = vpn + 1;
}
#[cfg(target_arch = "riscv64")]
unsafe {
core::arch::asm!("sfence.vma")
};
}
fn find_pte_mut(&self, vpn: VPN<Meta>) -> Option<*mut page_table::Pte<Meta>> {
let mut current = self.page_manager.root_ptr();
for level in (0..=Meta::MAX_LEVEL).rev() {
let idx = vpn.index_in(level);
let pte_ptr = unsafe { current.as_ptr().add(idx) };
let pte = unsafe { *pte_ptr };
if level == 0 {
return Some(pte_ptr);
}
if !pte.is_valid() {
return None;
}
let flags_raw = pte.flags().val();
let is_leaf = (flags_raw & 0b1010) != 0; if is_leaf {
return Some(pte_ptr);
}
current = self.page_manager.p_to_v(pte.ppn());
}
None
}
pub fn translate<T>(&self, addr: VAddr<Meta>, flags: VmFlags<Meta>) -> Option<NonNull<T>> {
let mut visitor = Visitor::new(self);
self.root().walk(Pos::new(addr.floor(), 0), &mut visitor);
visitor
.ans()
.filter(|pte| pte.flags().contains(flags))
.map(|pte| {
unsafe {
NonNull::new_unchecked(
self.page_manager
.p_to_v::<u8>(pte.ppn())
.as_ptr()
.add(addr.offset())
.cast(),
)
}
})
}
pub fn cloneself(&self, new_addrspace: &mut AddressSpace<Meta, M>) {
let root = self.root();
let areas = &self.areas;
for (_, range) in areas.iter().enumerate() {
let mut visitor = Visitor::new(self);
let vpn = range.start;
root.walk(Pos::new(vpn, 0), &mut visitor);
let (mut flags, mut data_ptr) = visitor
.ans()
.filter(|pte| pte.is_valid())
.map(|pte| {
(pte.flags(), unsafe {
NonNull::new_unchecked(self.page_manager.p_to_v::<u8>(pte.ppn()).as_ptr())
})
})
.unwrap();
let vpn_range = range.start..range.end;
let count = range.end.val() - range.start.val();
let size = count << Meta::PAGE_BITS;
let paddr = new_addrspace.page_manager.allocate(count, &mut flags);
let ppn = new_addrspace.page_manager.v_to_p(paddr);
unsafe {
use core::slice::from_raw_parts_mut as slice;
let data = slice(data_ptr.as_mut(), size);
let ptr = paddr.as_ptr();
slice(ptr, size).copy_from_slice(data);
}
new_addrspace.map_extern(vpn_range, ppn, flags);
}
}
}
impl<Meta: VmMeta, P: PageManager<Meta>> fmt::Debug for AddressSpace<Meta, P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "root: {:#x}", self.root_ppn().val())?;
write!(
f,
"{:?}",
PageTableFormatter {
pt: self.root(),
f: |ppn| self.page_manager.p_to_v(ppn)
}
)
}
}