diff --git a/ch4/os/src/mm/address.rs b/ch4/os/src/mm/address.rs index 49d4dca..7d0abbc 100644 --- a/ch4/os/src/mm/address.rs +++ b/ch4/os/src/mm/address.rs @@ -1,34 +1,41 @@ use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS}; +use crate::mm::page_table::PageTableEntry; 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 VPN_WIDTH_SV39: usize = VA_WIDTH_SV39 - PAGE_SIZE_BITS; // 虚拟页的宽度 27 -// 物理地址 +// 物理地址 56bit 44 12 #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct PhysAddr(pub usize); -// 虚拟地址 +// 虚拟地址 39bit 9 9 9 12 #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct VirtAddr(pub usize); -// 物理页 +// 物理页 44bit #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct PhysPageNum(pub usize); -// 虚拟页 +// 虚拟页 29bit 布局为 9 9 9 #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub struct VirtPageNum(pub usize); // 把usize 转为真实地址(只保留 PA_WIDTH_SV39 位字节) impl From 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类型的物理页号 转为物理页结构体 (只保留物理页的宽度) impl From 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, 直接返回即可 @@ -74,12 +81,12 @@ impl PhysAddr { impl PhysPageNum { // // 从当前物理页保存的数据中得到所有的 PTE - // pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] { - // // 物理页号 转为物理地址 - // let pa: PhysAddr = (*self).into(); - // // 从物理页地址读出一页 512个8字节(4k)的数据 - // unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) } - // } + pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] { + // 物理页号 转为物理地址 + let pa: PhysAddr = (*self).into(); + // 从物理页地址读出一页 512个8字节(4k)的数据 + unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) } + } // 从当前物理页得到 引用类型的 byte array pub fn get_bytes_array(&self) -> &'static mut [u8] { @@ -92,4 +99,45 @@ impl PhysPageNum { let pa: PhysAddr = (*self).into(); unsafe { (pa.0 as *mut T).as_mut().unwrap() } } +} + + +// 把usize 转为 虚拟地址, 只保留39位 +impl From for VirtAddr { + fn from(value: usize) -> Self { + Self(value & ((1 << PPN_WIDTH_SV39) - 1 )) + } +} + +// 把虚拟地址, 转为usize +impl From 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 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 + } } \ No newline at end of file diff --git a/ch4/os/src/mm/frame_allocator.rs b/ch4/os/src/mm/frame_allocator.rs index 3e3108c..3a87d30 100644 --- a/ch4/os/src/mm/frame_allocator.rs +++ b/ch4/os/src/mm/frame_allocator.rs @@ -10,7 +10,9 @@ extern "C" { } // 物理页帧的结构体, 他和物理页号不同, 它是管理整个物理页内的数据, 而物理页号被页表管理 +// 这个结构体只是利用rust drop管理变量的方式 // 我们使用这个结构体, 来利用rust变量生命周期, 自动管理该页, 数据清零或者还回物理页帧管理器 +// 这个结构可以省去(因为他很多时候使用PhysPageNum 也可以完成), 但是为了防止后续课程还有其他地方使用, 就不省了, pub struct FrameTracker { pub ppn: PhysPageNum, } diff --git a/ch4/os/src/mm/mod.rs b/ch4/os/src/mm/mod.rs index 9d15053..11b2e82 100644 --- a/ch4/os/src/mm/mod.rs +++ b/ch4/os/src/mm/mod.rs @@ -1,6 +1,7 @@ pub mod heap_allocator; pub mod address; pub mod frame_allocator; +pub mod page_table; @@ -9,6 +10,4 @@ pub fn init(){ heap_allocator::init_heap(); // 初始化bin测试 物理页帧管理器 frame_allocator::frame_allocator_test(); - - } \ No newline at end of file diff --git a/ch4/os/src/mm/page_table.rs b/ch4/os/src/mm/page_table.rs index 210bbd8..ea5ff9b 100644 --- a/ch4/os/src/mm/page_table.rs +++ b/ch4/os/src/mm/page_table.rs @@ -1,6 +1,8 @@ +use alloc::vec; use alloc::vec::Vec; use bitflags::*; -use crate::mm::address::PhysPageNum; +use crate::mm::address::{PhysPageNum, VirtAddr, VirtPageNum}; +use crate::mm::frame_allocator::{frame_alloc, FrameTracker}; bitflags! { @@ -72,6 +74,102 @@ impl PageTableEntry { // 页表 pub struct PageTable { root_ppn: PhysPageNum, - // 这里保存的是页表每一页所占的物理页帧的页号 + // 这里保存, 保存了 被映射出来的每级页表的物理帧, 页表被删除时, 页表所占用的物理帧 也被rust释放执行drop还给物理页帧管理器, FrameTracker 实现了 Drop trait frames: Vec, -} \ No newline at end of file +} + +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 { + self.find_pte(vpn).map(|pte| *pte) + } +} + +