255 lines
7.2 KiB
Rust
255 lines
7.2 KiB
Rust
use super::{
|
|
frame_alloc,
|
|
PhysPageNum,
|
|
FrameTracker,
|
|
VirtPageNum,
|
|
VirtAddr,
|
|
PhysAddr,
|
|
StepByOne
|
|
};
|
|
use alloc::vec::Vec;
|
|
use alloc::vec;
|
|
use alloc::string::String;
|
|
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<FrameTracker>,
|
|
}
|
|
|
|
/// 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<&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() {
|
|
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<PageTableEntry> {
|
|
self.find_pte(vpn)
|
|
.map(|pte| {pte.clone()})
|
|
}
|
|
pub fn translate_va(&self, va: VirtAddr) -> Option<PhysAddr> {
|
|
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<T>(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<T>(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<Self::Item> {
|
|
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)
|
|
}
|
|
}
|
|
}
|