Skip to content

Commit

Permalink
axmm: add page fault handler and implement lazy mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed Jul 4, 2024
1 parent c7e8c46 commit 032fe35
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 52 deletions.
5 changes: 5 additions & 0 deletions crates/memory_set/src/area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ impl<F: Copy, P, B: MappingBackend<F, P>> MemoryArea<F, P, B> {
pub const fn size(&self) -> usize {
self.va_range.size()
}

/// Returns the mapping backend of the memory area.
pub const fn backend(&self) -> &B {
&self.backend
}
}

impl<F: Copy, P, B: MappingBackend<F, P>> MemoryArea<F, P, B> {
Expand Down
90 changes: 66 additions & 24 deletions crates/page_table/src/bits64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,25 @@ impl<M: PagingMetaData, PTE: GenericPTE, IF: PagingIf> PageTable64<M, PTE, IF> {
Ok(())
}

/// Remap the mapping starts with `vaddr`, updates both the physical address
/// and flags.
///
/// Returns the page size of the mapping.
///
/// Returns [`Err(PagingError::NotMapped)`](PagingError::NotMapped) if the
/// intermediate level tables of the mapping is not present.
pub fn remap(
&mut self,
vaddr: VirtAddr,
paddr: PhysAddr,
flags: MappingFlags,
) -> PagingResult<PageSize> {
let (entry, size) = self.get_entry_mut(vaddr)?;
entry.set_paddr(paddr);
entry.set_flags(flags, size.is_huge());
Ok(size)
}

/// Unmaps the mapping starts with `vaddr`.
///
/// Returns [`Err(PagingError::NotMapped)`](PagingError::NotMapped) if the
Expand All @@ -92,46 +111,38 @@ impl<M: PagingMetaData, PTE: GenericPTE, IF: PagingIf> PageTable64<M, PTE, IF> {
Ok((paddr, size))
}

/// Query the result of the mapping starts with `vaddr`.
/// Updates the flags of the mapping starts with `vaddr`.
///
/// Returns the physical address of the target frame, mapping flags, and
/// the page size.
/// Returns the page size of the mapping.
///
/// Returns [`Err(PagingError::NotMapped)`](PagingError::NotMapped) if the
/// mapping is not present.
pub fn query(&self, vaddr: VirtAddr) -> PagingResult<(PhysAddr, MappingFlags, PageSize)> {
pub fn protect(&mut self, vaddr: VirtAddr, flags: MappingFlags) -> PagingResult<PageSize> {
let (entry, size) = self.get_entry_mut(vaddr)?;
if entry.is_unused() {
return Err(PagingError::NotMapped);
}
let off = vaddr.align_offset(size);
Ok((entry.paddr() + off, entry.flags(), size))
entry.set_flags(flags, size.is_huge());
Ok(size)
}

/// Updates the target or flags of the mapping starts with `vaddr`. If the
/// corresponding argument is `None`, it will not be updated.
/// Queries the result of the mapping starts with `vaddr`.
///
/// Returns the page size of the mapping.
/// Returns the physical address of the target frame, mapping flags, and
/// the page size.
///
/// Returns [`Err(PagingError::NotMapped)`](PagingError::NotMapped) if the
/// mapping is not present.
pub fn update(
&mut self,
vaddr: VirtAddr,
paddr: Option<PhysAddr>,
flags: Option<MappingFlags>,
) -> PagingResult<PageSize> {
pub fn query(&self, vaddr: VirtAddr) -> PagingResult<(PhysAddr, MappingFlags, PageSize)> {
let (entry, size) = self.get_entry_mut(vaddr)?;
if let Some(paddr) = paddr {
entry.set_paddr(paddr);
}
if let Some(flags) = flags {
entry.set_flags(flags, size.is_huge());
if entry.is_unused() {
return Err(PagingError::NotMapped);
}
Ok(size)
let off = vaddr.align_offset(size);
Ok((entry.paddr() + off, entry.flags(), size))
}

/// Map a contiguous virtual memory region to a contiguous physical memory
/// Maps a contiguous virtual memory region to a contiguous physical memory
/// region with the given mapping `flags`.
///
/// The virtual and physical memory regions start with `vaddr` and `paddr`
Expand Down Expand Up @@ -199,10 +210,10 @@ impl<M: PagingMetaData, PTE: GenericPTE, IF: PagingIf> PageTable64<M, PTE, IF> {
Ok(())
}

/// Unmap a contiguous virtual memory region.
/// Unmaps a contiguous virtual memory region.
///
/// The region must be mapped before using [`PageTable64::map_region`], or
/// unexpected behaviors may occur.
/// unexpected behaviors may occur. It can deal with huge pages automatically.
pub fn unmap_region(&mut self, vaddr: VirtAddr, size: usize) -> PagingResult {
trace!(
"unmap_region({:#x}) [{:#x}, {:#x})",
Expand All @@ -224,6 +235,37 @@ impl<M: PagingMetaData, PTE: GenericPTE, IF: PagingIf> PageTable64<M, PTE, IF> {
Ok(())
}

/// Updates mapping flags of a contiguous virtual memory region.
///
/// The region must be mapped before using [`PageTable64::map_region`], or
/// unexpected behaviors may occur. It can deal with huge pages automatically.
pub fn protect_region(
&mut self,
vaddr: VirtAddr,
size: usize,
flags: MappingFlags,
) -> PagingResult {
trace!(
"protect_region({:#x}) [{:#x}, {:#x}) {:?}",
self.root_paddr(),
vaddr,
vaddr + size,
flags,
);
let mut vaddr = vaddr;
let mut size = size;
while size > 0 {
let page_size = self
.protect(vaddr, flags)
.inspect_err(|e| error!("failed to protect page: {:#x?}, {:?}", vaddr, e))?;
assert!(vaddr.is_aligned(page_size));
assert!(page_size as usize <= size);
vaddr += page_size as usize;
size -= page_size as usize;
}
Ok(())
}

/// Walk the page table recursively.
///
/// When reaching the leaf page table, call `func` on the current page table
Expand Down
9 changes: 6 additions & 3 deletions crates/page_table_entry/src/arch/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ impl MemAttr {

impl From<DescriptorAttr> for MappingFlags {
fn from(attr: DescriptorAttr) -> Self {
let mut flags = Self::empty();
if attr.contains(DescriptorAttr::VALID) {
flags |= Self::READ;
if !attr.contains(DescriptorAttr::VALID) {
return Self::empty();
}
let mut flags = Self::READ;
if !attr.contains(DescriptorAttr::AP_RO) {
flags |= Self::WRITE;
}
Expand All @@ -139,6 +139,9 @@ impl From<DescriptorAttr> for MappingFlags {

impl From<MappingFlags> for DescriptorAttr {
fn from(flags: MappingFlags) -> Self {
if flags.is_empty() {
return Self::empty();
}
let mut attr = if flags.contains(MappingFlags::DEVICE) {
Self::from_mem_attr(MemAttr::Device)
} else if flags.contains(MappingFlags::UNCACHED) {
Expand Down
3 changes: 3 additions & 0 deletions crates/page_table_entry/src/arch/riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ bitflags::bitflags! {
impl From<PTEFlags> for MappingFlags {
fn from(f: PTEFlags) -> Self {
let mut ret = Self::empty();
if !f.contains(PTEFlags::V) {
return ret;
}
if f.contains(PTEFlags::R) {
ret |= Self::READ;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/page_table_entry/src/arch/x86_64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{GenericPTE, MappingFlags};

impl From<PTF> for MappingFlags {
fn from(f: PTF) -> Self {
if f.is_empty() {
if !f.contains(PTF::PRESENT) {
return Self::empty();
}
let mut ret = Self::READ;
Expand Down
44 changes: 34 additions & 10 deletions modules/axhal/src/arch/x86_64/trap.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use memory_addr::VirtAddr;
use x86::{controlregs::cr2, irq::*};
use x86_64::structures::idt::PageFaultErrorCode;

use super::context::TrapFrame;
use crate::trap::PageFaultFlags;

core::arch::global_asm!(include_str!("trap.S"));

Expand All @@ -14,18 +17,14 @@ const IRQ_VECTOR_END: u8 = 0xff;
fn x86_trap_handler(tf: &mut TrapFrame) {
match tf.vector as u8 {
PAGE_FAULT_VECTOR => {
if tf.is_user() {
warn!(
"User #PF @ {:#x}, fault_vaddr={:#x}, error_code={:#x}",
tf.rip,
unsafe { cr2() },
tf.error_code,
);
} else {
let vaddr = VirtAddr::from(unsafe { cr2() });
let access_flags = get_page_fault_flags(tf.error_code);
if !crate::trap::handle_page_fault(vaddr, access_flags, tf.is_user()) {
panic!(
"Kernel #PF @ {:#x}, fault_vaddr={:#x}, error_code={:#x}:\n{:#x?}",
"Unhandled {} #PF @ {:#x}, fault_vaddr={:#x}, error_code={:#x}:\n{:#x?}",
if tf.is_user() { "user" } else { "kernel" },
tf.rip,
unsafe { cr2() },
vaddr,
tf.error_code,
tf,
);
Expand Down Expand Up @@ -54,6 +53,31 @@ fn x86_trap_handler(tf: &mut TrapFrame) {
}
}

fn get_page_fault_flags(error_code: u64) -> PageFaultFlags {
let reserved_bits = (PageFaultErrorCode::CAUSED_BY_WRITE
| PageFaultErrorCode::USER_MODE
| PageFaultErrorCode::PROTECTION_VIOLATION)
.complement();
let code = PageFaultErrorCode::from_bits_truncate(error_code);
if code.intersects(reserved_bits) {
panic!("Invalid #PF error code: {:?}", code);
}

let mut flags = PageFaultFlags::empty();
if code.contains(PageFaultErrorCode::CAUSED_BY_WRITE) {
flags |= PageFaultFlags::WRITE;
} else {
flags |= PageFaultFlags::READ;
}
if code.contains(PageFaultErrorCode::USER_MODE) {
flags |= PageFaultFlags::USER;
}
if code.contains(PageFaultErrorCode::PROTECTION_VIOLATION) {
flags |= PageFaultFlags::EXECUTE;
}
flags
}

fn vec_to_str(vec: u64) -> &'static str {
if vec < 32 {
EXCEPTIONS[vec as usize].mnemonic
Expand Down
17 changes: 16 additions & 1 deletion modules/axhal/src/trap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use crate_interface::{call_interface, def_interface};

use crate::arch::TrapFrame;

pub use memory_addr::VirtAddr;
pub use page_table_entry::MappingFlags as PageFaultFlags;

/// Syscall handler interface.
#[def_interface]
pub trait SyscallHandler {
Expand All @@ -23,7 +26,9 @@ pub trait SyscallHandler {
pub trait TrapHandler {
/// Handles interrupt requests for the given IRQ number.
fn handle_irq(irq_num: usize);
// more e.g.: handle_page_fault();
/// Handles (kernel/user) page faults with the given accessed address and
/// flags. Returns `true` if it handled successfully.
fn handle_page_fault(vaddr: VirtAddr, access_flags: PageFaultFlags, is_user: bool) -> bool;
}

/// Call the external IRQ handler.
Expand All @@ -32,6 +37,16 @@ pub(crate) fn handle_irq_extern(irq_num: usize) {
call_interface!(TrapHandler::handle_irq, irq_num);
}

/// Call the external page fault handler.
#[allow(dead_code)]
pub(crate) fn handle_page_fault(
vaddr: VirtAddr,
access_flags: PageFaultFlags,
is_user: bool,
) -> bool {
call_interface!(TrapHandler::handle_page_fault, vaddr, access_flags, is_user)
}

/// Call the external syscall handler.
#[allow(dead_code)]
pub(crate) fn handle_syscall(tf: &TrapFrame, syscall_num: usize) -> isize {
Expand Down
22 changes: 22 additions & 0 deletions modules/axmm/src/aspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,28 @@ impl AddrSpace {
pub fn clear(&mut self) {
self.areas.clear(&mut self.pt).unwrap();
}

/// Handles a page fault at the given address.
///
/// `access_flags` indicates the access type that caused the page fault.
///
/// Returns `true` if the page fault is handled successfully (not a real
/// fault).
pub fn handle_page_fault(&mut self, vaddr: VirtAddr, access_flags: MappingFlags) -> bool {
if !self.va_range.contains(vaddr) {
return false;
}
if let Some(area) = self.areas.find(vaddr) {
let orig_flags = area.flags();
if !orig_flags.contains(access_flags) {
return false;
}
area.backend()
.handle_page_fault(vaddr, orig_flags, &mut self.pt)
} else {
false
}
}
}

impl fmt::Debug for AddrSpace {
Expand Down
Loading

0 comments on commit 032fe35

Please sign in to comment.