mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-26 10:53:25 +00:00
Split kernel mode and user mode page table
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
c26eac757a
commit
aeea333945
@ -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<PageTableEntry> = 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)
|
||||
|
@ -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<PageTableEntry> = 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())
|
||||
|
@ -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);
|
||||
|
@ -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 => {}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ pub static FRAMEBUFFER_REGIONS: Once<Vec<MemoryRegion>> = 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() {
|
||||
|
@ -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<SpinLock<PageTable<PageTableEntry, KernelMode>>> = Once::new();
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UserMode {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KernelMode {}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PageTable<T: PageTableEntryTrait> {
|
||||
pub root_pa: Paddr,
|
||||
pub struct PageTable<T: PageTableEntryTrait, M = UserMode> {
|
||||
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<VmFrame>,
|
||||
config: PageTableConfig,
|
||||
_phantom: PhantomData<T>,
|
||||
_phantom: PhantomData<(T, M)>,
|
||||
}
|
||||
|
||||
impl<T: PageTableEntryTrait> PageTable<T> {
|
||||
impl<T: PageTableEntryTrait> PageTable<T, UserMode> {
|
||||
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<T: PageTableEntryTrait> PageTable<T, KernelMode> {
|
||||
/// 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<T: PageTableEntryTrait, M> PageTable<T, M> {
|
||||
/// Add a new mapping directly in the root page table.
|
||||
///
|
||||
/// # Safety
|
||||
@ -149,12 +226,23 @@ impl<T: PageTableEntryTrait> PageTable<T> {
|
||||
///
|
||||
pub unsafe fn add_root_mapping(&mut self, index: usize, pte: &T) {
|
||||
debug_assert!((index + 1) * size_of::<T>() <= 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<T: PageTableEntryTrait> PageTable<T> {
|
||||
// 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>() * T::page_index(vaddr, count))
|
||||
&mut *(paddr_to_vaddr(self.root_paddr + size_of::<T>() * T::page_index(vaddr, count))
|
||||
as *mut T)
|
||||
};
|
||||
|
||||
@ -220,7 +308,13 @@ impl<T: PageTableEntryTrait> PageTable<T> {
|
||||
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<T: PageTableEntryTrait> PageTable<T> {
|
||||
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<T: PageTableEntryTrait> PageTable<T> {
|
||||
}
|
||||
|
||||
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<Paddr> {
|
||||
#[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<PageTableEntry> = 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,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user