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:
parent
3a57221785
commit
8107d0993c
@ -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");
|
||||||
|
@ -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();
|
||||||
|
@ -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> {
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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,10 +515,36 @@ 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"))]
|
||||||
if !handle_user_page_fault(&thread, addr) {
|
{
|
||||||
// TODO: SIGSEGV
|
use crate::arch::interrupt::consts::{
|
||||||
panic!("page fault handle failed");
|
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) {
|
||||||
|
// TODO: SIGSEGV
|
||||||
|
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,
|
||||||
|
Loading…
Reference in New Issue
Block a user