/// Allocate a physical page frame in FrameTracker style
pub fn frame_alloc() -> Option<FrameTracker> {
FRAME_ALLOCATOR
.exclusive_access()
.alloc()
.map(FrameTracker::new)
}
/// Deallocate a physical page frame with a given ppn
pub fn frame_dealloc(ppn: PhysPageNum) {
FRAME_ALLOCATOR.exclusive_access().dealloc(ppn);
}
impl Drop for FrameTracker {
fn drop(&mut self) {
frame_dealloc(self.ppn);
}
}
/// page table structure
pub struct PageTable {
root_ppn: PhysPageNum,
frames: Vec<FrameTracker>,
}
/// Assume that it won't oom when creating/mapping.
impl PageTable {
/// Create a new page table
pub fn new() -> Self {
let frame = frame_alloc().unwrap();
PageTable {
root_ppn: frame.ppn,
frames: vec![frame],
}
}
}
PageTable
内核中直接访问物理地址(恒等映射)
impl PhysPageNum {
/// Get the reference of page table(array of ptes)
pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] {
let pa: PhysAddr = (*self).into();
unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) }
}
/// Get the reference of page(array of bytes)
pub fn get_bytes_array(&self) -> &'static mut [u8] {
let pa: PhysAddr = (*self).into();
unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut u8, 4096) }
}
/// Get the mutable reference of physical address
pub fn get_mut<T>(&self) -> &'static mut T {
let pa: PhysAddr = (*self).into();
pa.get_mut()
}
}
impl VirtPageNum {
/// Get the indexes of the page table entry
pub fn indexes(&self) -> [usize; 3] {
let mut = self.0;
let mut idx = [0usize; 3];
for i in (0..3).rev() {
idx[i] = & 511;
>>= 9;
}
idx
}
}
/// Find PageTableEntry by VirtPageNum, create a frame for a 4KB page table if not exist
fn find_pte_create(&mut self, : VirtPageNum) -> Option<&mut PageTableEntry> {
let idxs = .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
}
/// Find PageTableEntry by VirtPageNum, create a frame for a 4KB page table if not exist
fn find_pte_create(&mut self, : VirtPageNum) -> Option<&mut PageTableEntry> {
let idxs = .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
}
/// set the map between virtual page number and physical page number
#[allow(unused)]
pub fn map(&mut self, : VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) {
let pte = self.find_pte_create().unwrap();
assert!(!pte.is_valid(), " {:?} is mapped before mapping", );
*pte = PageTableEntry::new(ppn, flags | PTEFlags::V);
}
/// remove the map between virtual page number and physical page number
#[allow(unused)]
pub fn unmap(&mut self, : VirtPageNum) {
let pte = self.find_pte().unwrap();
assert!(pte.is_valid(), " {:?} is invalid before unmapping", );
*pte = PageTableEntry::empty();
}
/// Mention that trampoline is not collected by areas.
fn map_trampoline(&mut self) {
self.page_table.map(
VirtAddr::from(TRAMPOLINE).into(),
PhysAddr::from(strampoline as usize).into(),
PTEFlags::R | PTEFlags::X,
);
}
.text : {
*(.text.entry)
. = ALIGN(4K);
strampoline = .;
*(.text.trampoline);
. = ALIGN(4K);
*(.text .text.*)
}
.section .text.trampoline
.globl __alltraps
.globl __restore
.align 2
__alltraps:
# ...
# load kernel_satp into t0
ld t0, 34*8(sp)
# load trap_handler into t1
ld t1, 36*8(sp)
# move to kernel_sp
ld sp, 35*8(sp)
# switch to kernel space
csrw satp, t0
sfence.vma
# jump to trap_handler
jr t1
__restore:
# a0: *TrapContext in user space(Constant); a1: user space token
# switch to user space
csrw satp, a1
sfence.vma
csrw sscratch, a0
/// finally, jump to new addr of __restore asm function
pub fn trap_return() -> ! {
set_user_trap_entry();
let trap_cx_ptr = TRAP_CONTEXT_BASE;
let user_satp = current_user_token();
extern "C" {
fn __alltraps();
fn __restore();
}
let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;
// trace!("[kernel] trap_return: ..before return");
unsafe {
asm!(
"fence.i",
"jr {restore_va}", // jump to new addr of __restore asm function
restore_va = in(reg) restore_va,
in("a0") trap_cx_ptr, // a0 = virt addr of Trap Context
in("a1") user_satp, // a1 = phy addr of usr page table
options(noreturn)
);
}
}