diff --git a/framework/jinux-frame/src/arch/x86/iommu/context_table.rs b/framework/jinux-frame/src/arch/x86/iommu/context_table.rs index c6be14e67..38e06ed44 100644 --- a/framework/jinux-frame/src/arch/x86/iommu/context_table.rs +++ b/framework/jinux-frame/src/arch/x86/iommu/context_table.rs @@ -6,7 +6,6 @@ use pod::Pod; use crate::{ bus::pci::PciDeviceLocation, - config::PAGE_SIZE, vm::{ page_table::{PageTableConfig, PageTableError}, Paddr, PageTable, Vaddr, VmAllocOptions, VmFrame, VmFrameVec, VmIo, @@ -61,14 +60,14 @@ impl RootTable { &mut self, device: PciDeviceLocation, vaddr: Vaddr, - paddr: Paddr, + frame: &VmFrame, ) -> Result<(), ContextTableError> { if device.device >= 32 || device.function >= 8 { return Err(ContextTableError::InvalidDeviceId); } self.get_or_create_context_table(device) - .map(device, vaddr, paddr & !(PAGE_SIZE - 1))?; + .map(device, vaddr, frame)?; Ok(()) } @@ -262,7 +261,7 @@ impl ContextTable { if !bus_entry.present() { let table: PageTable = PageTable::new(PageTableConfig { - address_width: crate::vm::page_table::AddressWidth::Level3PageTable, + address_width: crate::vm::page_table::AddressWidth::Level3, }); let address = table.root_paddr(); self.page_tables.insert(address, table); @@ -286,7 +285,7 @@ impl ContextTable { &mut self, device: PciDeviceLocation, vaddr: Vaddr, - paddr: Paddr, + frame: &VmFrame, ) -> Result<(), ContextTableError> { if device.device >= 32 || device.function >= 8 { return Err(ContextTableError::InvalidDeviceId); @@ -294,7 +293,7 @@ impl ContextTable { self.get_or_create_page_table(device) .map( vaddr, - paddr, + frame, PageTableFlags::WRITABLE | PageTableFlags::READABLE | PageTableFlags::LAST_PAGE, ) .map_err(ContextTableError::ModificationError) diff --git a/framework/jinux-frame/src/arch/x86/iommu/mod.rs b/framework/jinux-frame/src/arch/x86/iommu/mod.rs index a8ebdb63d..91ef2d0ed 100644 --- a/framework/jinux-frame/src/arch/x86/iommu/mod.rs +++ b/framework/jinux-frame/src/arch/x86/iommu/mod.rs @@ -3,7 +3,7 @@ mod fault; mod remapping; mod second_stage; -use crate::sync::Mutex; +use crate::{sync::Mutex, vm::VmFrame}; use log::info; use spin::Once; @@ -12,7 +12,7 @@ use crate::{ bus::pci::PciDeviceLocation, vm::{ page_table::{PageTableConfig, PageTableError}, - Paddr, PageTable, Vaddr, + PageTable, Vaddr, }, }; @@ -27,7 +27,7 @@ pub enum IommuError { /// # Safety /// /// Mapping an incorrect address may lead to a kernel data leak. -pub(crate) unsafe fn map(vaddr: Vaddr, paddr: Paddr) -> Result<(), IommuError> { +pub(crate) unsafe fn map(vaddr: Vaddr, frame: &VmFrame) -> Result<(), IommuError> { let Some(table) = PAGE_TABLE.get() else { return Err(IommuError::NoIommu); }; @@ -41,7 +41,7 @@ pub(crate) unsafe fn map(vaddr: Vaddr, paddr: Paddr) -> Result<(), IommuError> { function: 0, }, vaddr, - paddr, + frame, ) .map_err(|err| match err { context_table::ContextTableError::InvalidDeviceId => unreachable!(), @@ -78,7 +78,7 @@ pub(crate) fn init() -> Result<(), IommuError> { let mut root_table = RootTable::new(); // For all PCI Device, use the same page table. let page_table: PageTable = PageTable::new(PageTableConfig { - address_width: crate::vm::page_table::AddressWidth::Level3PageTable, + address_width: crate::vm::page_table::AddressWidth::Level3, }); for table in PciDeviceLocation::all() { root_table.specify_device_page_table(table, page_table.clone()) diff --git a/framework/jinux-frame/src/arch/x86/mm/mod.rs b/framework/jinux-frame/src/arch/x86/mm/mod.rs index 2e4f98899..0564ebb9a 100644 --- a/framework/jinux-frame/src/arch/x86/mm/mod.rs +++ b/framework/jinux-frame/src/arch/x86/mm/mod.rs @@ -45,6 +45,18 @@ pub fn tlb_flush(vaddr: Vaddr) { tlb::flush(VirtAddr::new(vaddr as u64)); } +pub const fn is_user_vaddr(vaddr: Vaddr) -> bool { + // FIXME: Support 3/5 level page table. + // 47 = 12(offset) + 4 * 9(index) - 1 + (vaddr >> 47) == 0 +} + +pub const fn is_kernel_vaddr(vaddr: Vaddr) -> bool { + // FIXME: Support 3/5 level page table. + // 47 = 12(offset) + 4 * 9(index) - 1 + ((vaddr >> 47) & 0x1) == 1 +} + #[derive(Clone, Copy, Pod)] #[repr(C)] pub struct PageTableEntry(usize); diff --git a/framework/jinux-frame/src/vm/frame.rs b/framework/jinux-frame/src/vm/frame.rs index 395b6f75e..be0c4cb8a 100644 --- a/framework/jinux-frame/src/vm/frame.rs +++ b/framework/jinux-frame/src/vm/frame.rs @@ -50,7 +50,7 @@ impl VmFrameVec { for frame in frames.iter_mut() { // Safety: The address is controlled by frame allocator. unsafe { - if let Err(err) = iommu::map(frame.start_paddr(), frame.start_paddr()) { + if let Err(err) = iommu::map(frame.start_paddr(), frame) { match err { // do nothing iommu::IommuError::NoIommu => {} diff --git a/framework/jinux-frame/src/vm/memory_set.rs b/framework/jinux-frame/src/vm/memory_set.rs index 03f13ad97..bc66125a1 100644 --- a/framework/jinux-frame/src/vm/memory_set.rs +++ b/framework/jinux-frame/src/vm/memory_set.rs @@ -163,9 +163,9 @@ impl MemorySet { // TODO: check overlap if let Entry::Vacant(e) = self.areas.entry(area.start_va) { let area = e.insert(area); - for (va, pa) in area.mapper.iter() { - debug_assert!(pa.start_paddr() < PHYS_OFFSET); - self.pt.map(*va, pa.start_paddr(), area.flags).unwrap(); + for (va, frame) in area.mapper.iter() { + debug_assert!(frame.start_paddr() < PHYS_OFFSET); + self.pt.map(*va, frame, area.flags).unwrap(); } } else { panic!( @@ -191,7 +191,7 @@ impl MemorySet { pub fn new() -> Self { let mut page_table = PageTable::new(PageTableConfig { - address_width: super::page_table::AddressWidth::Level4PageTable, + address_width: super::page_table::AddressWidth::Level4, }); let mapped_pte = crate::arch::mm::ALL_MAPPED_PTE.lock(); for (index, pte) in mapped_pte.iter() { @@ -300,7 +300,7 @@ impl fmt::Debug for MemorySet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("MemorySet") .field("areas", &self.areas) - .field("page_table_root", &self.pt.root_pa) + .field("page_table_root", &self.pt.root_paddr()) .finish() } } diff --git a/framework/jinux-frame/src/vm/mod.rs b/framework/jinux-frame/src/vm/mod.rs index 0f02fb3e6..80d82e8f3 100644 --- a/framework/jinux-frame/src/vm/mod.rs +++ b/framework/jinux-frame/src/vm/mod.rs @@ -63,6 +63,7 @@ pub static FRAMEBUFFER_REGIONS: Once> = Once::new(); pub(crate) fn init() { let memory_regions = crate::boot::memory_regions().to_owned(); frame_allocator::init(&memory_regions); + page_table::init(); let mut framebuffer_regions = Vec::new(); for i in memory_regions.iter() { diff --git a/framework/jinux-frame/src/vm/page_table.rs b/framework/jinux-frame/src/vm/page_table.rs index 4d625d476..7b0245d49 100644 --- a/framework/jinux-frame/src/vm/page_table.rs +++ b/framework/jinux-frame/src/vm/page_table.rs @@ -3,14 +3,16 @@ use super::{ frame_allocator, paddr_to_vaddr, VmAllocOptions, VmFrameVec, {Paddr, Vaddr}, }; use crate::{ - arch::mm::{tlb_flush, PageTableEntry}, + arch::mm::{is_kernel_vaddr, is_user_vaddr, tlb_flush, PageTableEntry}, config::{ENTRY_COUNT, PAGE_SIZE}, + sync::SpinLock, vm::VmFrame, }; use alloc::{vec, vec::Vec}; use core::{fmt::Debug, marker::PhantomData, mem::size_of}; use log::trace; use pod::Pod; +use spin::Once; pub trait PageTableFlagsTrait: Clone + Copy + Sized + Pod + Debug { fn new() -> Self; @@ -89,11 +91,10 @@ pub struct PageTableConfig { #[derive(Debug, Clone, Copy)] #[repr(usize)] -#[allow(clippy::enum_variant_names)] pub enum AddressWidth { - Level3PageTable = 3, - Level4PageTable = 4, - Level5PageTable = 5, + Level3 = 3, + Level4 = 4, + Level5 = 5, } #[derive(Debug)] @@ -104,43 +105,119 @@ pub enum PageTableError { /// 2. The mapping is already invalid before unmap operation. /// 3. The mapping is not exists before protect operation. InvalidModification, + InvalidVaddr, } +pub static KERNEL_PAGE_TABLE: Once>> = Once::new(); + +#[derive(Clone)] +pub struct UserMode {} + +#[derive(Clone)] +pub struct KernelMode {} + #[derive(Clone, Debug)] -pub struct PageTable { - pub root_pa: Paddr, +pub struct PageTable { + root_paddr: Paddr, /// store all the physical frame that the page table need to map all the frame e.g. the frame of the root_pa tables: Vec, config: PageTableConfig, - _phantom: PhantomData, + _phantom: PhantomData<(T, M)>, } -impl PageTable { +impl PageTable { pub fn new(config: PageTableConfig) -> Self { let root_frame = frame_allocator::alloc_zero(VmFrameFlags::empty()).unwrap(); Self { - root_pa: root_frame.start_paddr(), + root_paddr: root_frame.start_paddr(), tables: vec![root_frame], config, _phantom: PhantomData, } } - /// Create the page table structure according to the physical address, note that the created page table can only use the page_walk function without create. + pub fn map( + &mut self, + vaddr: Vaddr, + frame: &VmFrame, + flags: T::F, + ) -> Result<(), PageTableError> { + if is_kernel_vaddr(vaddr) { + return Err(PageTableError::InvalidVaddr); + } + // Safety: + // 1. The vaddr belongs to user mode program and does not affect the kernel mapping. + // 2. The area where the physical address islocated at untyped memory and does not affect kernel security. + unsafe { self.do_map(vaddr, frame.start_paddr(), flags) } + } + + pub fn unmap(&mut self, vaddr: Vaddr) -> Result<(), PageTableError> { + if is_kernel_vaddr(vaddr) { + return Err(PageTableError::InvalidVaddr); + } + // Safety: The vaddr belongs to user mode program and does not affect the kernel mapping. + unsafe { self.do_unmap(vaddr) } + } + + pub fn protect(&mut self, vaddr: Vaddr, flags: T::F) -> Result<(), PageTableError> { + if is_kernel_vaddr(vaddr) { + return Err(PageTableError::InvalidVaddr); + } + // Safety: The vaddr belongs to user mode program and does not affect the kernel mapping. + unsafe { self.do_protect(vaddr, flags) } + } +} + +impl PageTable { + /// Mapping `vaddr` to `paddr` with flags. The `vaddr` should not be at the low address + /// (memory belonging to the user mode program). /// /// # Safety /// - /// User should ensure the physical address is valid and only invoke the `page_walk` function without creating new PTE. - /// - pub unsafe fn from_paddr(config: PageTableConfig, paddr: Paddr) -> Self { - Self { - root_pa: paddr, - tables: Vec::new(), - config, - _phantom: PhantomData, + /// Modifying kernel mappings is considered unsafe, and incorrect operation may cause crashes. + /// User must take care of the consequences when using this API. + pub unsafe fn map( + &mut self, + vaddr: Vaddr, + paddr: Paddr, + flags: T::F, + ) -> Result<(), PageTableError> { + if is_user_vaddr(vaddr) { + return Err(PageTableError::InvalidVaddr); } + self.do_map(vaddr, paddr, flags) } + /// Unmap `vaddr`. The `vaddr` should not be at the low address + /// (memory belonging to the user mode program). + /// + /// # Safety + /// + /// Modifying kernel mappings is considered unsafe, and incorrect operation may cause crashes. + /// User must take care of the consequences when using this API. + pub unsafe fn unmap(&mut self, vaddr: Vaddr) -> Result<(), PageTableError> { + if is_user_vaddr(vaddr) { + return Err(PageTableError::InvalidVaddr); + } + self.do_unmap(vaddr) + } + + /// Modify the flags mapped at `vaddr`. The `vaddr` should not be at the low address + /// (memory belonging to the user mode program). + /// + /// # Safety + /// + /// Modifying kernel mappings is considered unsafe, and incorrect operation may cause crashes. + /// User must take care of the consequences when using this API. + pub unsafe fn protect(&mut self, vaddr: Vaddr, flags: T::F) -> Result<(), PageTableError> { + if is_user_vaddr(vaddr) { + return Err(PageTableError::InvalidVaddr); + } + self.do_protect(vaddr, flags) + } +} + +impl PageTable { /// Add a new mapping directly in the root page table. /// /// # Safety @@ -149,12 +226,23 @@ impl PageTable { /// pub unsafe fn add_root_mapping(&mut self, index: usize, pte: &T) { debug_assert!((index + 1) * size_of::() <= PAGE_SIZE); - // Safety: The root_pa is refer to the root of a valid page table. - let root_ptes: &mut [T] = table_of(self.root_pa).unwrap(); + // Safety: The root_paddr is refer to the root of a valid page table. + let root_ptes: &mut [T] = table_of(self.root_paddr).unwrap(); root_ptes[index] = *pte; } - pub fn map(&mut self, vaddr: Vaddr, paddr: Paddr, flags: T::F) -> Result<(), PageTableError> { + /// Mapping `vaddr` to `paddr` with flags. + /// + /// # Safety + /// + /// This function allows arbitrary modifications to the page table. + /// Incorrect modifications may cause the kernel to crash (e.g., changing the linear mapping.). + unsafe fn do_map( + &mut self, + vaddr: Vaddr, + paddr: Paddr, + flags: T::F, + ) -> Result<(), PageTableError> { let last_entry = self.page_walk(vaddr, true).unwrap(); trace!( "Page Table: Map vaddr:{:x?}, paddr:{:x?}, flags:{:x?}", @@ -181,7 +269,7 @@ impl PageTable { // Safety: The offset does not exceed the value of PAGE_SIZE. // It only change the memory controlled by page table. let mut current: &mut T = unsafe { - &mut *(paddr_to_vaddr(self.root_pa + size_of::() * T::page_index(vaddr, count)) + &mut *(paddr_to_vaddr(self.root_paddr + size_of::() * T::page_index(vaddr, count)) as *mut T) }; @@ -220,7 +308,13 @@ impl PageTable { Some(current) } - pub fn unmap(&mut self, vaddr: Vaddr) -> Result<(), PageTableError> { + /// Unmap `vaddr`. + /// + /// # Safety + /// + /// This function allows arbitrary modifications to the page table. + /// Incorrect modifications may cause the kernel to crash (e.g., unmap the linear mapping.). + unsafe fn do_unmap(&mut self, vaddr: Vaddr) -> Result<(), PageTableError> { let last_entry = self.page_walk(vaddr, false).unwrap(); trace!("Page Table: Unmap vaddr:{:x?}", vaddr); if last_entry.is_unused() && !last_entry.flags().is_present() { @@ -231,7 +325,14 @@ impl PageTable { Ok(()) } - pub fn protect(&mut self, vaddr: Vaddr, flags: T::F) -> Result<(), PageTableError> { + /// Modify the flags mapped at `vaddr` + /// + /// # Safety + /// + /// This function allows arbitrary modifications to the page table. + /// Incorrect modifications may cause the kernel to crash + /// (e.g., make the linear mapping visible to the user mode applications.). + unsafe fn do_protect(&mut self, vaddr: Vaddr, flags: T::F) -> Result<(), PageTableError> { let last_entry = self.page_walk(vaddr, false).unwrap(); trace!("Page Table: Protect vaddr:{:x?}, flags:{:x?}", vaddr, flags); if last_entry.is_unused() || !last_entry.flags().is_present() { @@ -243,7 +344,7 @@ impl PageTable { } pub fn root_paddr(&self) -> Paddr { - self.root_pa + self.root_paddr } } @@ -263,22 +364,25 @@ pub unsafe fn table_of<'a, T: PageTableEntryTrait>(pa: Paddr) -> Option<&'a mut /// translate a virtual address to physical address which cannot use offset to get physical address pub fn vaddr_to_paddr(vaddr: Vaddr) -> Option { - #[cfg(target_arch = "x86_64")] - let (page_directory_base, _) = x86_64::registers::control::Cr3::read(); - - // TODO: Read and use different level page table. - // Safety: The page_directory_base is valid since it is read from PDBR. - // We only use this instance to do the page walk without creating. - let mut page_table: PageTable = unsafe { - PageTable::from_paddr( - PageTableConfig { - address_width: AddressWidth::Level4PageTable, - }, - page_directory_base.start_address().as_u64() as usize, - ) - }; - let page_directory_base = page_directory_base.start_address().as_u64() as usize; + let mut page_table = KERNEL_PAGE_TABLE.get().unwrap().lock(); + // Although we bypass the unsafe APIs provided by KernelMode, the purpose here is + // only to obtain the corresponding physical address according to the mapping. let last_entry = page_table.page_walk(vaddr, false)?; // FIXME: Support huge page Some(last_entry.paddr() + (vaddr & (PAGE_SIZE - 1))) } + +pub fn init() { + KERNEL_PAGE_TABLE.call_once(|| { + #[cfg(target_arch = "x86_64")] + let (page_directory_base, _) = x86_64::registers::control::Cr3::read(); + SpinLock::new(PageTable { + root_paddr: page_directory_base.start_address().as_u64() as usize, + tables: Vec::new(), + config: PageTableConfig { + address_width: AddressWidth::Level4, + }, + _phantom: PhantomData, + }) + }); +} diff --git a/framework/jinux-frame/src/vm/space.rs b/framework/jinux-frame/src/vm/space.rs index 0738a8815..89a3df99b 100644 --- a/framework/jinux-frame/src/vm/space.rs +++ b/framework/jinux-frame/src/vm/space.rs @@ -40,7 +40,7 @@ impl VmSpace { pub unsafe fn activate(&self) { #[cfg(target_arch = "x86_64")] crate::arch::x86::mm::activate_page_table( - self.memory_set.lock().pt.root_pa, + self.memory_set.lock().pt.root_paddr(), x86_64::registers::control::Cr3Flags::PAGE_LEVEL_CACHE_DISABLE, ); }