use super::{ PageTable, PTEFlags, VirtAddr, VirtPageNum, PhysAddr, PhysPageNum, FrameTracker, VPNRange, frame_alloc, }; use core::ops::Range; use alloc::collections::BTreeMap; use alloc::vec::Vec; use riscv::register::satp; use alloc::sync::Arc; use lazy_static::*; use spin::Mutex; use crate::config::MEMORY_END; extern "C" { fn stext(); fn etext(); fn srodata(); fn erodata(); fn sdata(); fn edata(); fn sbss_with_stack(); fn ebss(); fn ekernel(); } lazy_static! { pub static ref KERNEL_SPACE: Arc> = Arc::new(Mutex::new( MemorySet::new_kernel() )); } pub struct MemorySet { page_table: PageTable, areas: Vec, } impl MemorySet { pub fn new_bare() -> Self { Self { page_table: PageTable::new(), areas: Vec::new(), } } fn push(&mut self, mut map_area: MapArea) { map_area.map(&mut self.page_table); self.areas.push(map_area); } pub fn new_kernel() -> Self { let mut memory_set = Self::new_bare(); 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::new( (stext as usize).into(), (etext as usize).into(), MapType::Identical, MapPermission::R | MapPermission::X, )); println!("mapping .rodata section"); memory_set.push(MapArea::new( (srodata as usize).into(), (erodata as usize).into(), MapType::Identical, MapPermission::R, )); println!("mapping .data section"); memory_set.push(MapArea::new( (sdata as usize).into(), (edata as usize).into(), MapType::Identical, MapPermission::R | MapPermission::W, )); println!("mapping .bss section"); memory_set.push(MapArea::new( (sbss_with_stack as usize).into(), (ebss as usize).into(), MapType::Identical, MapPermission::R | MapPermission::W, )); println!("mapping physical memory"); memory_set.push(MapArea::new( (ekernel as usize).into(), MEMORY_END.into(), MapType::Identical, MapPermission::R | MapPermission::W, )); memory_set } pub fn activate(&self) { let satp = self.page_table.token(); unsafe { satp::write(satp); llvm_asm!("sfence.vma" :::: "volatile"); } } } pub struct MapArea { vpn_range: VPNRange, data_frames: BTreeMap, map_type: MapType, map_perm: MapPermission, } impl MapArea { pub fn new( start_va: VirtAddr, end_va: VirtAddr, map_type: MapType, map_perm: MapPermission ) -> Self { // alignment assertion, limit to kernel remapping let start_vpn: VirtPageNum = start_va.into(); let end_vpn: VirtPageNum = end_va.into(); Self { vpn_range: VPNRange::new(start_vpn, end_vpn), data_frames: BTreeMap::new(), map_type, map_perm, } } pub fn map_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) { let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap(); let mut ppn = PhysPageNum(0); match self.map_type { MapType::Identical => { ppn = PhysPageNum(vpn.0); } MapType::Framed => { let frame = frame_alloc().unwrap(); ppn = frame.ppn; self.data_frames.insert(vpn, frame); } } page_table.map(vpn, ppn, pte_flags); } pub fn unmap_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) { match self.map_type { MapType::Framed => { self.data_frames.remove(&vpn); } _ => {} } 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 enum MapType { Identical, Framed, } bitflags! { pub struct MapPermission: u8 { const R = 1 << 1; const W = 1 << 2; const X = 1 << 3; } } #[allow(unused)] pub fn remap_test() { let mut kernel_space = KERNEL_SPACE.lock(); let mid_text: VirtAddr = ((stext as usize + etext as usize) / 2).into(); let mid_rodata: VirtAddr = ((srodata as usize + erodata as usize) / 2).into(); let mid_data: VirtAddr = ((sdata as usize + edata as usize) / 2).into(); assert_eq!( kernel_space.page_table.translate(mid_text.floor()).unwrap().writable(), false ); assert_eq!( kernel_space.page_table.translate(mid_rodata.floor()).unwrap().writable(), false, ); assert_eq!( kernel_space.page_table.translate(mid_data.floor()).unwrap().executable(), false, ); println!("remap_test passed!"); }