增加 PageTable 结构体相关

ch4
zhangxinyu 2 years ago
parent d4069b8cde
commit deec9b74af

@ -1,34 +1,41 @@
use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS}; use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS};
use crate::mm::page_table::PageTableEntry;
const PA_WIDTH_SV39: usize = 56; // 真实物理地址 的字节宽度 44(物理页宽) + 12(4kb的宽度) const PA_WIDTH_SV39: usize = 56; // 真实物理地址 的字节宽度 44(物理页宽) + 12(4kb的宽度)
const VA_WIDTH_SV39: usize = 39; // 虚拟地址的宽度 9 + 9 + 9 + 12
const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS; // 物理页 的宽度, 也就是 物理地址宽度-每页宽度=物理页宽度 const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS; // 物理页 的宽度, 也就是 物理地址宽度-每页宽度=物理页宽度
const VPN_WIDTH_SV39: usize = VA_WIDTH_SV39 - PAGE_SIZE_BITS; // 虚拟页的宽度 27
// 物理地址 // 物理地址 56bit 44 12
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct PhysAddr(pub usize); pub struct PhysAddr(pub usize);
// 虚拟地址 // 虚拟地址 39bit 9 9 9 12
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct VirtAddr(pub usize); pub struct VirtAddr(pub usize);
// 物理页 // 物理页 44bit
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct PhysPageNum(pub usize); pub struct PhysPageNum(pub usize);
// 虚拟页 // 虚拟页 29bit 布局为 9 9 9
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct VirtPageNum(pub usize); pub struct VirtPageNum(pub usize);
// 把usize 转为真实地址(只保留 PA_WIDTH_SV39 位字节) // 把usize 转为真实地址(只保留 PA_WIDTH_SV39 位字节)
impl From<usize> for PhysAddr { impl From<usize> for PhysAddr {
fn from(v: usize) -> Self { Self(v & ( (1 << PA_WIDTH_SV39) - 1 )) } fn from(v: usize) -> Self {
Self(v & ( (1 << PA_WIDTH_SV39) - 1 ))
}
} }
// 把usize类型的物理页号 转为物理页结构体 (只保留物理页的宽度) // 把usize类型的物理页号 转为物理页结构体 (只保留物理页的宽度)
impl From<usize> for PhysPageNum { impl From<usize> for PhysPageNum {
fn from(v: usize) -> Self { Self(v & ( (1 << PPN_WIDTH_SV39) - 1 )) } fn from(v: usize) -> Self {
Self(v & ( (1 << PPN_WIDTH_SV39) - 1 ))
}
} }
// 把物理地址转为 usize, 直接返回即可 // 把物理地址转为 usize, 直接返回即可
@ -74,12 +81,12 @@ impl PhysAddr {
impl PhysPageNum { impl PhysPageNum {
// // 从当前物理页保存的数据中得到所有的 PTE // // 从当前物理页保存的数据中得到所有的 PTE
// pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] { pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] {
// // 物理页号 转为物理地址 // 物理页号 转为物理地址
// let pa: PhysAddr = (*self).into(); let pa: PhysAddr = (*self).into();
// // 从物理页地址读出一页 512个8字节(4k)的数据 // 从物理页地址读出一页 512个8字节(4k)的数据
// unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) } unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) }
// } }
// 从当前物理页得到 引用类型的 byte array // 从当前物理页得到 引用类型的 byte array
pub fn get_bytes_array(&self) -> &'static mut [u8] { pub fn get_bytes_array(&self) -> &'static mut [u8] {
@ -92,4 +99,45 @@ impl PhysPageNum {
let pa: PhysAddr = (*self).into(); let pa: PhysAddr = (*self).into();
unsafe { (pa.0 as *mut T).as_mut().unwrap() } unsafe { (pa.0 as *mut T).as_mut().unwrap() }
} }
}
// 把usize 转为 虚拟地址, 只保留39位
impl From<usize> for VirtAddr {
fn from(value: usize) -> Self {
Self(value & ((1 << PPN_WIDTH_SV39) - 1 ))
}
}
// 把虚拟地址, 转为usize
impl From<VirtAddr> for usize {
fn from(v: VirtAddr) -> Self {
if v.0 >= (1 << (VA_WIDTH_SV39 - 1)) {
// 将高位全部设置为1, 虚拟地址就是这样的, 64~39 位, 随着38位变化
v.0 | (!((1 << VA_WIDTH_SV39) - 1))
} else {
v.0
}
}
}
// 把usize类型的页号 转为 虚拟页号结构体, 只保留27位即可
impl From<usize> for VirtPageNum {
fn from(v: usize) -> Self {
Self(v & ((1 << VPN_WIDTH_SV39) - 1))
}
}
impl VirtPageNum{
// 从虚拟页号中 抽离 9 9 9 的布局
pub fn indexes(&self) -> [usize; 3] {
let mut vpn = self.0;
let mut idx = [0usize; 3];
for i in (0..3).rev() {
idx[i] = vpn & 511;
vpn >>= 9;
}
idx
}
} }

@ -10,7 +10,9 @@ extern "C" {
} }
// 物理页帧的结构体, 他和物理页号不同, 它是管理整个物理页内的数据, 而物理页号被页表管理 // 物理页帧的结构体, 他和物理页号不同, 它是管理整个物理页内的数据, 而物理页号被页表管理
// 这个结构体只是利用rust drop管理变量的方式
// 我们使用这个结构体, 来利用rust变量生命周期, 自动管理该页, 数据清零或者还回物理页帧管理器 // 我们使用这个结构体, 来利用rust变量生命周期, 自动管理该页, 数据清零或者还回物理页帧管理器
// 这个结构可以省去(因为他很多时候使用PhysPageNum 也可以完成), 但是为了防止后续课程还有其他地方使用, 就不省了,
pub struct FrameTracker { pub struct FrameTracker {
pub ppn: PhysPageNum, pub ppn: PhysPageNum,
} }

@ -1,6 +1,7 @@
pub mod heap_allocator; pub mod heap_allocator;
pub mod address; pub mod address;
pub mod frame_allocator; pub mod frame_allocator;
pub mod page_table;
@ -9,6 +10,4 @@ pub fn init(){
heap_allocator::init_heap(); heap_allocator::init_heap();
// 初始化bin测试 物理页帧管理器 // 初始化bin测试 物理页帧管理器
frame_allocator::frame_allocator_test(); frame_allocator::frame_allocator_test();
} }

@ -1,6 +1,8 @@
use alloc::vec;
use alloc::vec::Vec; use alloc::vec::Vec;
use bitflags::*; use bitflags::*;
use crate::mm::address::PhysPageNum; use crate::mm::address::{PhysPageNum, VirtAddr, VirtPageNum};
use crate::mm::frame_allocator::{frame_alloc, FrameTracker};
bitflags! { bitflags! {
@ -72,6 +74,102 @@ impl PageTableEntry {
// 页表 // 页表
pub struct PageTable { pub struct PageTable {
root_ppn: PhysPageNum, root_ppn: PhysPageNum,
// 这里保存的是页表每一页所占的物理页帧的页号 // 这里保存, 保存了 被映射出来的每级页表的物理帧, 页表被删除时, 页表所占用的物理帧 也被rust释放执行drop还给物理页帧管理器, FrameTracker 实现了 Drop trait
frames: Vec<FrameTracker>, frames: Vec<FrameTracker>,
} }
impl PageTable {
// 创建根节点页表
pub fn new() -> Self {
let frame = frame_alloc().unwrap();
PageTable {
root_ppn: frame.ppn,
frames: vec![frame],
}
}
// 从stap寄存器保存的特定格式的token, 转为根节点页表
pub fn from_token(satp: usize) -> Self {
Self {
// 只保留44位 bit, 这44位bit, 就是 页表所在的物理页帧
root_ppn: PhysPageNum::from(satp & ((1usize << 44) - 1)),
frames: Vec::new(),
}
}
}
impl PageTable {
// 得到该页表被设置到 stap 需要的格式
// 注意, 这个只能是根节点才会用得到
pub fn token(&self) -> usize {
// 8 表示MODE, SV39分页机制开启
// 4bit 16bit 44bit 构成页表的 stap CSR的格式要求
8usize << 60 | self.root_ppn.0
}
// 寻找 指定页号, 如果不存在中间节点, 则创建出来, 知道找到三级页表的 指定下标 的 pte的指针
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() {
// 得到一级页表的pte
let pte = &mut ppn.get_pte_array()[*idx];
// 到达最后一级, 不管有无数据直接 跳出, 返回 三级页表保存的信息 0或者指向 物理页44bit+权限10bit 的数据
if i == 2 {
result = Some(pte);
break;
}
// 如果没有中间节点
if pte.is_valid() == false {
let frame = frame_alloc().unwrap();
*pte = PageTableEntry::from(frame.ppn, PTEFlags::V);
self.frames.push(frame);
}
ppn = pte.ppn();
}
result
}
// 和 find_pte_create 差不多, 只不过这个不创建中间节点, 缺中间节点立马就返回
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() == false {
return None;
}
ppn = pte.ppn();
}
result
}
// 把虚拟页 映射到页表中, 并创建一个 物理页, 然后把物理页ppn组成pte的格式, copy到 虚拟页对应的页表项内
pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) {
// 查找vpn 没有中间节点就创建出来
let pte = self.find_pte_create(vpn).unwrap();
assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn.0);
*pte = PageTableEntry::from(ppn, flags | PTEFlags::V);
}
// 把虚拟页 取消映射(注意, 取消之后, 外部一定要 把该虚拟页对应的物理页帧 还给全局的物理页帧管理器)
pub fn unmap(&mut self, vpn: VirtPageNum) {
let pte = self.find_pte(vpn).unwrap();
assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn.0);
*pte = PageTableEntry::new();
}
// 根据虚拟页号, 找到页表中对应的pte
pub fn get_pte(&self, vpn: VirtPageNum) -> Option<PageTableEntry> {
self.find_pte(vpn).map(|pte| *pte)
}
}

Loading…
Cancel
Save