添加地址空间以及创建内核地址空间的方法
							parent
							
								
									deec9b74af
								
							
						
					
					
						commit
						bb03c2e3e2
					
				| @ -0,0 +1,261 @@ | ||||
| use alloc::collections::BTreeMap; | ||||
| use alloc::vec::Vec; | ||||
| use bitflags::bitflags; | ||||
| use crate::config::{MEMORY_END, PAGE_SIZE, TRAMPOLINE}; | ||||
| use crate::mm::address::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum, VPNRange}; | ||||
| use crate::mm::frame_allocator::{frame_alloc, FrameTracker}; | ||||
| use crate::mm::page_table::{PageTable, PTEFlags}; | ||||
| use crate::println; | ||||
| 
 | ||||
| // 引入外部符号进来, 下面映射地址空间的逻辑段会用到
 | ||||
| extern "C" { | ||||
|     fn stext(); | ||||
|     fn etext(); | ||||
|     fn srodata(); | ||||
|     fn erodata(); | ||||
|     fn sdata(); | ||||
|     fn edata(); | ||||
|     fn sbss_with_stack(); | ||||
|     fn ebss(); | ||||
|     fn ekernel(); | ||||
|     fn strampoline();  // 这个符号 在linker.ld 中定义
 | ||||
| } | ||||
| 
 | ||||
| // 某逻辑段的映射方式
 | ||||
| #[derive(Copy, Clone, PartialEq, Debug)] | ||||
| pub enum MapType { | ||||
|     Identical,  // 恒等映射
 | ||||
|     Framed,  // 逻辑映射
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // 这个段的权限, 它是页表项标志位 PTEFlags 的一个子集, 仅仅保留4个标志位就足够了
 | ||||
| bitflags! { | ||||
|     /// map permission corresponding to that in pte: `R W X U`
 | ||||
|     pub struct MapPermission: u8 { | ||||
|         const R = 1 << 1; | ||||
|         const W = 1 << 2; | ||||
|         const X = 1 << 3; | ||||
|         const U = 1 << 4; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // 代表一个逻辑段
 | ||||
| pub struct MapArea { | ||||
|     vpn_range: VPNRange,  // 逻辑段的虚拟页起始位置 和 结束位置
 | ||||
|     data_frames: BTreeMap<VirtPageNum, FrameTracker>,  // 这个逻辑段   虚拟页 对应的具体的物理帧, 这里使用rust 进行管理其物理帧的释放, 自动drop进行还给物理页帧管理器
 | ||||
|     map_type: MapType, | ||||
|     map_perm: MapPermission, | ||||
| } | ||||
| 
 | ||||
| impl MapArea { | ||||
|     // 从虚拟地址起始和结束, 创建一个逻辑段结构体
 | ||||
|     pub fn from(start_va: VirtAddr, end_va: VirtAddr, map_type: MapType, map_perm: MapPermission,) -> Self { | ||||
|         // 截取抛弃内存碎片
 | ||||
|         let start_vpn: VirtPageNum = start_va.floor(); | ||||
|         let end_vpn: VirtPageNum = end_va.ceil(); | ||||
| 
 | ||||
|         Self { | ||||
|             vpn_range: VPNRange {l:start_vpn, r: end_vpn}, | ||||
|             data_frames: BTreeMap::new(), | ||||
|             map_type, | ||||
|             map_perm, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl MapArea { | ||||
|     // 把一个虚拟页号 创建一个对应的物理页帧 映射到page_table中, 并把创建的物理页帧生命周期 绑定到 self上
 | ||||
|     pub fn map_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) { | ||||
|         let ppn: PhysPageNum; | ||||
|         match self.map_type { | ||||
|             // 恒等映射
 | ||||
|             MapType::Identical => { | ||||
|                 ppn = PhysPageNum(vpn.0); | ||||
|             } | ||||
|             MapType::Framed => { | ||||
|                 // 随便创建一个 物理页帧
 | ||||
|                 let frame = frame_alloc().unwrap(); | ||||
|                 ppn = frame.ppn; | ||||
|                 // 把随便创建的物理页帧, 绑定到data_frames 中被 self管理生命周期
 | ||||
|                 self.data_frames.insert(vpn, frame); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // 根据上面创建的物理页帧映射pte 到三级页表中
 | ||||
|         let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap(); | ||||
|         page_table.map(vpn, ppn, pte_flags); | ||||
|     } | ||||
| 
 | ||||
|     // 取消映射一个vpn, 并把 self管理的 物理页帧的生命周期结束
 | ||||
|     pub fn unmap_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) { | ||||
|         if self.map_type == MapType::Framed { | ||||
|             // 删除映射关系, 自动调用 drop 把vpn对应的ppn还给全局内存管理器
 | ||||
|             self.data_frames.remove(&vpn); | ||||
|         } | ||||
|         // 物理帧不存在了 把vpn 对应的pte设置为空
 | ||||
|         page_table.unmap(vpn); | ||||
|     } | ||||
| 
 | ||||
|     // 映射 当前逻辑段中的每一个虚拟页, 到页表中
 | ||||
|     pub fn map(&mut self, page_table: &mut PageTable) { | ||||
|         for vpn in self.vpn_range { | ||||
|             self.map_one(page_table, vpn); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 在页表中取消映射每一个虚拟页
 | ||||
|     pub fn unmap(&mut self, page_table: &mut PageTable) { | ||||
|         for vpn in self.vpn_range { | ||||
|             self.unmap_one(page_table, vpn); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 拷贝外部的数据二进制 到当前的逻辑段中 按照页 为单位
 | ||||
|     pub fn copy_data(&mut self, page_table: &mut PageTable, data: &[u8]) { | ||||
|         assert_eq!(self.map_type, MapType::Framed); | ||||
| 
 | ||||
|         let mut start: usize = 0; | ||||
| 
 | ||||
|         // 当前逻辑段的起始页位置
 | ||||
|         let mut current_vpn = self.vpn_range.l; | ||||
|         // 需要拷贝的字节总大小
 | ||||
|         let len = data.len(); | ||||
|         loop { | ||||
|             // 拷贝 每次最大拷贝1个页
 | ||||
|             let src = &data[start..len.min(start + PAGE_SIZE)]; | ||||
| 
 | ||||
|             // 得到逻辑段每个页的起始位置
 | ||||
|             let dst = &mut page_table | ||||
|                 .get_pte(current_vpn)  // 在页表中, 根据逻辑段中的vpn 得到 54bit的pte (44+10)
 | ||||
|                 .unwrap() | ||||
|                 .ppn()  // 根据54bit的pte, 得到44bit 的物理页帧的位置
 | ||||
|                 .get_bytes_array()[..src.len()];  // 得到物理页帧那一页的起始的地址 44bit<<12
 | ||||
|             dst.copy_from_slice(src); | ||||
|             start += PAGE_SIZE; | ||||
|             if start >= len { | ||||
|                 break; | ||||
|             } | ||||
|             current_vpn = (current_vpn.0 + 1).into(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // 表示一个地址空间, 它是一个页表和 包含所有段信息的页表组成
 | ||||
| pub struct MemorySet { | ||||
|     page_table: PageTable, | ||||
|     areas: Vec<MapArea>, | ||||
| } | ||||
| 
 | ||||
| impl MemorySet { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             page_table: PageTable::new(), | ||||
|             areas: Vec::new(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl MemorySet { | ||||
|     // 得到当前地址空间的 页表token
 | ||||
|     pub fn token(&self) -> usize { | ||||
|         self.page_table.token() | ||||
|     } | ||||
| 
 | ||||
|     // 根据 start va 和end va 创建一个 逻辑段类型为逻辑映射, 插入到 当前地址空间
 | ||||
|     pub fn insert_framed_area(&mut self, start_va: VirtAddr, end_va: VirtAddr, permission: MapPermission) { | ||||
|         self.push(MapArea::from(start_va, end_va, MapType::Framed, permission), None); | ||||
|     } | ||||
| 
 | ||||
|     // 把一个 逻辑段, 映射到当前页表中
 | ||||
|     fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) { | ||||
|         map_area.map(&mut self.page_table); | ||||
|         if let Some(data) = data { | ||||
|             map_area.copy_data(&mut self.page_table, data); | ||||
|         } | ||||
|         self.areas.push(map_area); | ||||
|     } | ||||
| 
 | ||||
|     /// 创建跳板, 跳板就是 trap.S中的代码段中.text.trampoline, 由 linker.ld手动布局在代码段
 | ||||
|     /// 内核空间和用户空间,他们都会运行这个函数, 插入同样的代码, 把虚拟地址映射为同样的物理地址, 这里内核空间和用户空间执行这段代码的时候, 每个  虚拟地址对应的物理地址是相同的
 | ||||
|     /// 所以 (内核空间的 TRAMPOLINE+指令offset) == (用户空间的 TRAMPOLINE+offset)  == (strampoline + offset)
 | ||||
|     /// (内核的 TRAMPOLINE) 和 (用户的 TRAMPOLINE) 和 strampoline 他们都是相等的
 | ||||
|     /// 在 trap.S 切换用户空间之后, 取值执行, 切换空间之前和切换空间之后, 他们对应的虚拟地址空间是相同的, 且都映射到了同一个物理页帧
 | ||||
|     fn map_trampoline(&mut self) { | ||||
|         // 直接插入到 把 linker.ld 中的符号strampoline, 映射到最高的虚拟页, 他的物理页帧是 链接脚本中我们手动内存布局的符号
 | ||||
|         // 如果是用户应用, 这个符号的最下面就是 次高页 即 trap context
 | ||||
|         self.page_table.map( | ||||
|             VirtAddr::from(TRAMPOLINE).into(), | ||||
|             PhysAddr::from(strampoline as usize).into(), | ||||
|             PTEFlags::R | PTEFlags::X, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     // 创建一个内核用的地址空间
 | ||||
|     pub fn new_kernel() -> Self { | ||||
|         let mut memory_set = Self::new(); | ||||
|         // 跳板创建
 | ||||
|         memory_set.map_trampoline(); | ||||
|         // map kernel sections
 | ||||
|         println!(".text [{:#x}, {:#x})", stext as usize, etext as usize); | ||||
|         println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize); | ||||
|         println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize); | ||||
|         println!( | ||||
|             ".bss [{:#x}, {:#x})", | ||||
|             sbss_with_stack as usize, ebss as usize | ||||
|         ); | ||||
|         println!("mapping .text section"); | ||||
|         memory_set.push( | ||||
|             MapArea::from( | ||||
|                 (stext as usize).into(), | ||||
|                 (etext as usize).into(), | ||||
|                 MapType::Identical, | ||||
|                 MapPermission::R | MapPermission::X, | ||||
|             ), | ||||
|             None, | ||||
|         ); | ||||
|         println!("mapping .rodata section"); | ||||
|         memory_set.push( | ||||
|             MapArea::from( | ||||
|                 (srodata as usize).into(), | ||||
|                 (erodata as usize).into(), | ||||
|                 MapType::Identical, | ||||
|                 MapPermission::R, | ||||
|             ), | ||||
|             None, | ||||
|         ); | ||||
|         println!("mapping .data section"); | ||||
|         memory_set.push( | ||||
|             MapArea::from( | ||||
|                 (sdata as usize).into(), | ||||
|                 (edata as usize).into(), | ||||
|                 MapType::Identical, | ||||
|                 MapPermission::R | MapPermission::W, | ||||
|             ), | ||||
|             None, | ||||
|         ); | ||||
|         println!("mapping .bss section"); | ||||
|         memory_set.push( | ||||
|             MapArea::from( | ||||
|                 (sbss_with_stack as usize).into(), | ||||
|                 (ebss as usize).into(), | ||||
|                 MapType::Identical, | ||||
|                 MapPermission::R | MapPermission::W, | ||||
|             ), | ||||
|             None, | ||||
|         ); | ||||
|         println!("mapping physical memory"); | ||||
|         memory_set.push( | ||||
|             MapArea::from( | ||||
|                 (ekernel as usize).into(), | ||||
|                 MEMORY_END.into(), | ||||
|                 MapType::Identical, | ||||
|                 MapPermission::R | MapPermission::W, | ||||
|             ), | ||||
|             None, | ||||
|         ); | ||||
|         memory_set | ||||
|     } | ||||
| } | ||||
					Loading…
					
					
				
		Reference in New Issue