use super::{frame_alloc, PhysPageNum, FrameTracker, VirtPageNum, VirtAddr, StepByOne}; use alloc::vec::Vec; use alloc::vec; use bitflags::*; bitflags! { pub struct PTEFlags: u8 { const V = 1 << 0; const R = 1 << 1; const W = 1 << 2; const X = 1 << 3; const U = 1 << 4; const G = 1 << 5; const A = 1 << 6; const D = 1 << 7; } } #[derive(Copy, Clone)] #[repr(C)] pub struct PageTableEntry { pub bits: usize, } impl PageTableEntry { pub fn new(ppn: PhysPageNum, flags: PTEFlags) -> Self { PageTableEntry { bits: ppn.0 << 10 | flags.bits as usize, } } pub fn empty() -> Self { PageTableEntry { bits: 0, } } pub fn ppn(&self) -> PhysPageNum { (self.bits >> 10 & ((1usize << 44) - 1)).into() } pub fn flags(&self) -> PTEFlags { PTEFlags::from_bits(self.bits as u8).unwrap() } pub fn is_valid(&self) -> bool { (self.flags() & PTEFlags::V) != PTEFlags::empty() } pub fn readable(&self) -> bool { (self.flags() & PTEFlags::R) != PTEFlags::empty() } pub fn writable(&self) -> bool { (self.flags() & PTEFlags::W) != PTEFlags::empty() } pub fn executable(&self) -> bool { (self.flags() & PTEFlags::X) != PTEFlags::empty() } } pub struct PageTable { root_ppn: PhysPageNum, frames: Vec, } /// Assume that it won't oom when creating/mapping. impl PageTable { pub fn new() -> Self { let frame = frame_alloc().unwrap(); PageTable { root_ppn: frame.ppn, frames: vec![frame], } } /// Temporarily used to get arguments from user space. pub fn from_token(satp: usize) -> Self { Self { root_ppn: PhysPageNum::from(satp & ((1usize << 44) - 1)), frames: Vec::new(), } } fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> { let idxs = vpn.indexes(); let mut ppn = self.root_ppn; let mut result: Option<&mut PageTableEntry> = None; for i in 0..3 { let pte = &mut ppn.get_pte_array()[idxs[i]]; if i == 2 { result = Some(pte); break; } if !pte.is_valid() { let frame = frame_alloc().unwrap(); *pte = PageTableEntry::new(frame.ppn, PTEFlags::V); self.frames.push(frame); } ppn = pte.ppn(); } result } fn find_pte(&self, vpn: VirtPageNum) -> Option<&PageTableEntry> { let idxs = vpn.indexes(); let mut ppn = self.root_ppn; let mut result: Option<&PageTableEntry> = None; for i in 0..3 { let pte = &ppn.get_pte_array()[idxs[i]]; if i == 2 { result = Some(pte); break; } if !pte.is_valid() { return None; } ppn = pte.ppn(); } result } #[allow(unused)] pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) { let pte = self.find_pte_create(vpn).unwrap(); assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn); *pte = PageTableEntry::new(ppn, flags | PTEFlags::V); } #[allow(unused)] pub fn unmap(&mut self, vpn: VirtPageNum) { let pte = self.find_pte_create(vpn).unwrap(); assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn); *pte = PageTableEntry::empty(); } pub fn translate(&self, vpn: VirtPageNum) -> Option { self.find_pte(vpn) .map(|pte| {pte.clone()}) } pub fn token(&self) -> usize { 8usize << 60 | self.root_ppn.0 } } pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> { let page_table = PageTable::from_token(token); let mut start = ptr as usize; let end = start + len; let mut v = Vec::new(); while start < end { let start_va = VirtAddr::from(start); let mut vpn = start_va.floor(); let ppn = page_table .translate(vpn) .unwrap() .ppn(); vpn.step(); let mut end_va: VirtAddr = vpn.into(); end_va = end_va.min(VirtAddr::from(end)); v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]); start = end_va.into(); } v }