1
0
mirror of https://github.com/rcore-os/rCore.git synced 2024-11-21 23:56:18 +04:00

bugfix for race demand-page access.

This commit is contained in:
gjz010 2021-05-11 01:57:34 +08:00
parent 3a57221785
commit 8107d0993c
8 changed files with 148 additions and 22 deletions

View File

@ -49,10 +49,20 @@ impl<T: FrameAllocator> MemoryHandler for Delay<T> {
} }
} }
fn handle_page_fault(&self, pt: &mut dyn PageTable, addr: VirtAddr) -> bool { fn handle_page_fault_ext(
&self,
pt: &mut dyn PageTable,
addr: VirtAddr,
access: super::AccessType,
) -> bool {
let entry = pt.get_entry(addr).expect("failed to get entry"); let entry = pt.get_entry(addr).expect("failed to get entry");
if entry.present() { if entry.present() {
// not a delay case // permission check.
if access.check_access(entry) {
return true;
}
// permisison check failed.
error!("Permission check failed at 0x{:x}.", addr);
return false; return false;
} }
let frame = self.allocator.alloc().expect("failed to alloc frame"); let frame = self.allocator.alloc().expect("failed to alloc frame");

View File

@ -58,10 +58,24 @@ impl<F: Read, T: FrameAllocator> MemoryHandler for File<F, T> {
} }
} }
fn handle_page_fault(&self, pt: &mut dyn PageTable, addr: usize) -> bool { fn handle_page_fault_ext(
&self,
pt: &mut dyn PageTable,
addr: usize,
access: super::AccessType,
) -> bool {
let addr = addr & !(PAGE_SIZE - 1); let addr = addr & !(PAGE_SIZE - 1);
let entry = pt.get_entry(addr).expect("failed to get entry"); let entry = pt.get_entry(addr).expect("failed to get entry");
if entry.present() { if entry.present() {
// permission check.
if access.check_access(entry) {
return true;
}
// permisison check failed.
error!(
"Permission check failed at 0x{:x}, access = {:?}.",
addr, access
);
return false; return false;
} }
let execute = entry.execute(); let execute = entry.execute();

View File

@ -1,5 +1,45 @@
use super::*; use super::*;
#[derive(Copy, Clone, Debug)]
pub struct AccessType {
pub write: bool,
pub execute: bool,
pub user: bool,
}
impl AccessType {
pub fn unknown() -> Self {
AccessType {
write: true,
execute: true,
user: true,
}
}
pub fn read(user: bool) -> Self {
AccessType {
write: false,
execute: false,
user,
}
}
pub fn write(user: bool) -> Self {
AccessType {
write: true,
execute: false,
user,
}
}
pub fn execute(user: bool) -> Self {
AccessType {
write: false,
execute: true,
user,
}
}
pub fn check_access(self, entry: &dyn paging::Entry) -> bool {
((!self.write) || entry.writable())
&& ((!self.execute) || entry.execute())
&& ((!self.user) || entry.user())
}
}
// here may be a interesting part for lab // here may be a interesting part for lab
pub trait MemoryHandler: Debug + Send + Sync + 'static { pub trait MemoryHandler: Debug + Send + Sync + 'static {
fn box_clone(&self) -> Box<dyn MemoryHandler>; fn box_clone(&self) -> Box<dyn MemoryHandler>;
@ -22,7 +62,20 @@ pub trait MemoryHandler: Debug + Send + Sync + 'static {
/// Handle page fault on `addr` /// Handle page fault on `addr`
/// Return true if success, false if error /// Return true if success, false if error
fn handle_page_fault(&self, pt: &mut dyn PageTable, addr: VirtAddr) -> bool; fn handle_page_fault(&self, pt: &mut dyn PageTable, addr: VirtAddr) -> bool {
self.handle_page_fault_ext(pt, addr, AccessType::unknown())
}
/// Handle page fault on `addr` and access type `access`
/// Return true if success (or should-retry), false if error
fn handle_page_fault_ext(
&self,
pt: &mut dyn PageTable,
addr: VirtAddr,
_access: AccessType,
) -> bool {
self.handle_page_fault(pt, addr)
}
} }
impl Clone for Box<dyn MemoryHandler> { impl Clone for Box<dyn MemoryHandler> {

View File

@ -376,6 +376,15 @@ impl<T: PageTableExt> MemorySet<T> {
&mut self.page_table &mut self.page_table
} }
pub fn handle_page_fault_ext(&mut self, addr: VirtAddr, access: handler::AccessType) -> bool {
let area = self.areas.iter().find(|area| area.contains(addr));
match area {
Some(area) => area
.handler
.handle_page_fault_ext(&mut self.page_table, addr, access),
None => false,
}
}
pub fn handle_page_fault(&mut self, addr: VirtAddr) -> bool { pub fn handle_page_fault(&mut self, addr: VirtAddr) -> bool {
let area = self.areas.iter().find(|area| area.contains(addr)); let area = self.areas.iter().find(|area| area.contains(addr));
match area { match area {

View File

@ -14,6 +14,15 @@ pub fn is_page_fault(trap: usize) -> bool {
trap == InstructionPageFault || trap == LoadPageFault || trap == StorePageFault trap == InstructionPageFault || trap == LoadPageFault || trap == StorePageFault
} }
pub fn is_execute_page_fault(trap: usize) -> bool {
trap == InstructionPageFault
}
pub fn is_read_page_fault(trap: usize) -> bool {
trap == LoadPageFault
}
pub fn is_write_page_fault(trap: usize) -> bool {
trap == StorePageFault
}
pub fn is_syscall(trap: usize) -> bool { pub fn is_syscall(trap: usize) -> bool {
trap == Syscall trap == Syscall
} }

View File

@ -39,20 +39,27 @@ pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
trap_handler_no_frame(&mut tf.sepc); trap_handler_no_frame(&mut tf.sepc);
} }
use crate::memory::AccessType;
#[inline] #[inline]
pub fn trap_handler_no_frame(sepc: &mut usize) { pub fn trap_handler_no_frame(sepc: &mut usize) {
use self::scause::{Exception as E, Interrupt as I, Trap}; use self::scause::{Exception as E, Interrupt as I, Trap};
let scause = scause::read(); let scause = scause::read();
let stval = stval::read(); let stval = stval::read();
let is_user = false;
trace!("Interrupt @ CPU{}: {:?} ", super::cpu::id(), scause.cause()); trace!("Interrupt @ CPU{}: {:?} ", super::cpu::id(), scause.cause());
match scause.cause() { match scause.cause() {
Trap::Interrupt(I::SupervisorExternal) => external(), Trap::Interrupt(I::SupervisorExternal) => external(),
Trap::Interrupt(I::SupervisorSoft) => ipi(), Trap::Interrupt(I::SupervisorSoft) => ipi(),
Trap::Interrupt(I::SupervisorTimer) => timer(), Trap::Interrupt(I::SupervisorTimer) => timer(),
Trap::Exception(E::LoadPageFault) => page_fault(stval, sepc), Trap::Exception(E::LoadPageFault) => page_fault(stval, sepc, AccessType::read(is_user)),
Trap::Exception(E::StorePageFault) => page_fault(stval, sepc), Trap::Exception(E::StorePageFault) => page_fault(stval, sepc, AccessType::write(is_user)),
Trap::Exception(E::InstructionPageFault) => page_fault(stval, sepc), Trap::Exception(E::InstructionPageFault) => {
_ => panic!("unhandled trap {:?}", scause.cause()), page_fault(stval, sepc, AccessType::execute(is_user))
}
_ => {
let bits = scause.bits();
panic!("unhandled trap {:?} ({})", scause.cause(), bits);
}
} }
trace!("Interrupt end"); trace!("Interrupt end");
} }
@ -62,7 +69,6 @@ fn external() {
unsafe { unsafe {
super::board::handle_external_interrupt(); super::board::handle_external_interrupt();
} }
IRQ_MANAGER IRQ_MANAGER
.read() .read()
.try_handle_interrupt(Some(SupervisorExternal)); .try_handle_interrupt(Some(SupervisorExternal));
@ -78,11 +84,11 @@ pub fn timer() {
crate::trap::timer(); crate::trap::timer();
} }
fn page_fault(stval: usize, sepc: &mut usize) { fn page_fault(stval: usize, sepc: &mut usize, access: AccessType) {
let addr = stval; let addr = stval;
info!("\nEXCEPTION: Page Fault @ {:#x}", addr); info!("\nEXCEPTION: Page Fault @ {:#x}", addr);
if crate::memory::handle_page_fault(addr) { if crate::memory::handle_page_fault_ext(addr, access) {
return; return;
} }
extern "C" { extern "C" {
@ -94,6 +100,7 @@ fn page_fault(stval: usize, sepc: &mut usize) {
*sepc = crate::memory::read_user_fixup as usize; *sepc = crate::memory::read_user_fixup as usize;
return; return;
} }
error!("unhandled page fault {:#x} from {:#x}", addr, sepc);
panic!("unhandled page fault"); panic!("unhandled page fault");
} }
@ -121,8 +128,8 @@ pub fn wait_for_interrupt() {
} }
} }
pub fn handle_user_page_fault(thread: &Arc<Thread>, addr: usize) -> bool { pub fn handle_user_page_fault_ext(thread: &Arc<Thread>, addr: usize, access: AccessType) -> bool {
thread.vm.lock().handle_page_fault(addr) thread.vm.lock().handle_page_fault_ext(addr, access)
} }
pub fn handle_reserved_inst(tf: &mut UserContext) -> bool { pub fn handle_reserved_inst(tf: &mut UserContext) -> bool {

View File

@ -5,7 +5,7 @@ use super::{
use crate::arch::interrupt::consts::{ use crate::arch::interrupt::consts::{
is_intr, is_page_fault, is_reserved_inst, is_syscall, is_timer_intr, is_intr, is_page_fault, is_reserved_inst, is_syscall, is_timer_intr,
}; };
use crate::arch::interrupt::{get_trap_num, handle_reserved_inst, handle_user_page_fault}; use crate::arch::interrupt::{get_trap_num, handle_reserved_inst};
use crate::arch::{ use crate::arch::{
cpu, cpu,
fp::FpState, fp::FpState,
@ -505,10 +505,8 @@ pub fn spawn(thread: Arc<Thread>) {
thread_context.fp.restore(); thread_context.fp.restore();
cx.run(); cx.run();
thread_context.fp.save(); thread_context.fp.save();
let trap_num = get_trap_num(&cx); let trap_num = get_trap_num(&cx);
trace!("back from user: {:#x?} trap_num {:#x}", cx, trap_num); trace!("back from user: {:#x?} trap_num {:#x}", cx, trap_num);
let mut exit = false; let mut exit = false;
let mut do_yield = false; let mut do_yield = false;
match trap_num { match trap_num {
@ -517,12 +515,38 @@ pub fn spawn(thread: Arc<Thread>) {
// page fault // page fault
let addr = get_page_fault_addr(); let addr = get_page_fault_addr();
info!("page fault from user @ {:#x}", addr); info!("page fault from user @ {:#x}", addr);
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
{
use crate::arch::interrupt::consts::{
is_execute_page_fault, is_read_page_fault, is_write_page_fault,
};
use crate::arch::interrupt::handle_user_page_fault_ext;
let access_type = match trap_num {
_ if is_execute_page_fault(trap_num) => {
crate::memory::AccessType::execute(true)
}
_ if is_read_page_fault(trap_num) => {
crate::memory::AccessType::read(true)
}
_ if is_write_page_fault(trap_num) => {
crate::memory::AccessType::write(true)
}
_ => unreachable!(),
};
if !handle_user_page_fault_ext(&thread, addr, access_type) {
// TODO: SIGSEGV
panic!("page fault handle failed");
}
}
#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
{
use crate::arch::interrupt::handle_user_page_fault;
if !handle_user_page_fault(&thread, addr) { if !handle_user_page_fault(&thread, addr) {
// TODO: SIGSEGV // TODO: SIGSEGV
panic!("page fault handle failed"); panic!("page fault handle failed");
} }
} }
}
_ if is_syscall(trap_num) => exit = handle_syscall(&thread, cx).await, _ if is_syscall(trap_num) => exit = handle_syscall(&thread, cx).await,
_ if is_intr(trap_num) => { _ if is_intr(trap_num) => {
crate::arch::interrupt::ack(trap_num); crate::arch::interrupt::ack(trap_num);