use super::{frame_alloc, FrameTracker, PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum}; use alloc::string::String; use alloc::vec; use alloc::vec::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, idx) in idxs.iter().enumerate() { let pte = &mut ppn.get_pte_array()[*idx]; 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<&mut PageTableEntry> { let idxs = vpn.indexes(); let mut ppn = self.root_ppn; let mut result: Option<&mut PageTableEntry> = None; for (i, idx) in idxs.iter().enumerate() { let pte = &mut ppn.get_pte_array()[*idx]; 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(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) } pub fn translate_va(&self, va: VirtAddr) -> Option { self.find_pte(va.clone().floor()).map(|pte| { let aligned_pa: PhysAddr = pte.ppn().into(); let offset = va.page_offset(); let aligned_pa_usize: usize = aligned_pa.into(); (aligned_pa_usize + offset).into() }) } 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)); if end_va.page_offset() == 0 { v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..]); } else { v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]); } start = end_va.into(); } v } /// Load a string from other address spaces into kernel space without an end `\0`. pub fn translated_str(token: usize, ptr: *const u8) -> String { let page_table = PageTable::from_token(token); let mut string = String::new(); let mut va = ptr as usize; loop { let ch: u8 = *(page_table .translate_va(VirtAddr::from(va)) .unwrap() .get_mut()); if ch == 0 { break; } string.push(ch as char); va += 1; } string } pub fn translated_ref(token: usize, ptr: *const T) -> &'static T { let page_table = PageTable::from_token(token); page_table .translate_va(VirtAddr::from(ptr as usize)) .unwrap() .get_ref() } pub fn translated_refmut(token: usize, ptr: *mut T) -> &'static mut T { let page_table = PageTable::from_token(token); let va = ptr as usize; page_table .translate_va(VirtAddr::from(va)) .unwrap() .get_mut() } pub struct UserBuffer { pub buffers: Vec<&'static mut [u8]>, } impl UserBuffer { pub fn new(buffers: Vec<&'static mut [u8]>) -> Self { Self { buffers } } pub fn len(&self) -> usize { let mut total: usize = 0; for b in self.buffers.iter() { total += b.len(); } total } } impl IntoIterator for UserBuffer { type Item = *mut u8; type IntoIter = UserBufferIterator; fn into_iter(self) -> Self::IntoIter { UserBufferIterator { buffers: self.buffers, current_buffer: 0, current_idx: 0, } } } pub struct UserBufferIterator { buffers: Vec<&'static mut [u8]>, current_buffer: usize, current_idx: usize, } impl Iterator for UserBufferIterator { type Item = *mut u8; fn next(&mut self) -> Option { if self.current_buffer >= self.buffers.len() { None } else { let r = &mut self.buffers[self.current_buffer][self.current_idx] as *mut _; if self.current_idx + 1 == self.buffers[self.current_buffer].len() { self.current_idx = 0; self.current_buffer += 1; } else { self.current_idx += 1; } Some(r) } } }