You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
4.7 KiB

#[cfg(feature = "m_mode")]
use riscv::register::{
mstatus as xstatus,
mscratch as xscratch,
mtvec as xtvec,
};
#[cfg(not(feature = "m_mode"))]
use riscv::register::{
sstatus as xstatus,
sscratch as xscratch,
stvec as xtvec,
};
use riscv::register::{mcause, mepc, sie, mie};
use crate::drivers::DRIVERS;
pub use self::context::*;
use log::*;
#[path = "context.rs"]
mod context;
/*
* @brief:
* initialize the interrupt status
*/
pub fn init() {
extern {
fn __alltraps();
}
unsafe {
// Set sscratch register to 0, indicating to exception vector that we are
// presently executing in the kernel
xscratch::write(0);
// Set the exception vector address
xtvec::write(__alltraps as usize, xtvec::TrapMode::Direct);
// Enable IPI
sie::set_ssoft();
// Enable serial interrupt
#[cfg(feature = "m_mode")]
mie::set_mext();
#[cfg(not(feature = "m_mode"))]
sie::set_sext();
// NOTE: In M-mode: mie.MSIE is set by BBL.
// mie.MEIE can not be set in QEMU v3.0
// (seems like a bug)
}
info!("interrupt: init end");
}
/*
* @brief:
* enable interrupt
*/
#[inline(always)]
pub unsafe fn enable() {
xstatus::set_xie();
}
/*
* @brief:
* store and disable interrupt
* @retbal:
* a usize value store the origin sie
*/
#[inline(always)]
pub unsafe fn disable_and_store() -> usize {
let e = xstatus::read().xie() as usize;
xstatus::clear_xie();
e
}
/*
* @param:
* flags: input flag
* @brief:
* enable interrupt if flags != 0
*/
#[inline(always)]
pub unsafe fn restore(flags: usize) {
if flags != 0 {
xstatus::set_xie();
}
}
/*
* @param:
* TrapFrame: the trapFrame of the Interrupt/Exception/Trap to be processed
* @brief:
* process the Interrupt/Exception/Trap
*/
#[no_mangle]
pub extern fn rust_trap(tf: &mut TrapFrame) {
use self::mcause::{Trap, Interrupt as I, Exception as E};
trace!("Interrupt @ CPU{}: {:?} ", super::cpu::id(), tf.scause.cause());
match tf.scause.cause() {
// M-mode only
Trap::Interrupt(I::MachineExternal) => external(),
Trap::Interrupt(I::MachineSoft) => ipi(),
Trap::Interrupt(I::MachineTimer) => timer(),
Trap::Exception(E::MachineEnvCall) => sbi(tf),
Trap::Interrupt(I::SupervisorExternal) => external(),
Trap::Interrupt(I::SupervisorSoft) => ipi(),
Trap::Interrupt(I::SupervisorTimer) => timer(),
Trap::Exception(E::IllegalInstruction) => illegal_inst(tf),
Trap::Exception(E::UserEnvCall) => syscall(tf),
Trap::Exception(E::LoadPageFault) => page_fault(tf),
Trap::Exception(E::StorePageFault) => page_fault(tf),
Trap::Exception(E::InstructionPageFault) => page_fault(tf),
_ => crate::trap::error(tf),
}
trace!("Interrupt end");
}
/// Call BBL SBI functions for M-mode kernel
fn sbi(tf: &mut TrapFrame) {
(super::BBL.mcall_trap)(tf.x.as_ptr(), tf.scause.bits(), tf.sepc);
tf.sepc += 4;
}
fn external() {
// true means handled, false otherwise
let handlers = [try_process_serial, try_process_drivers];
for handler in handlers.iter() {
if handler() == true {
break
}
}
}
fn try_process_serial() -> bool {
match super::io::getchar_option() {
Some(ch) => {
crate::trap::serial(ch);
true
}
None => false
}
}
fn try_process_drivers() -> bool {
for driver in DRIVERS.read().iter() {
if driver.try_handle_interrupt() == true {
return true
}
}
return false
}
fn ipi() {
debug!("IPI");
bbl::sbi::clear_ipi();
}
/*
* @brief:
* process timer interrupt
*/
fn timer() {
super::timer::set_next();
crate::trap::timer();
}
/*
* @param:
* TrapFrame: the Trapframe for the syscall
* @brief:
* process syscall
*/
fn syscall(tf: &mut TrapFrame) {
tf.sepc += 4; // Must before syscall, because of fork.
let ret = crate::syscall::syscall(tf.x[10], [tf.x[11], tf.x[12], tf.x[13], tf.x[14], tf.x[15], tf.x[16]], tf);
tf.x[10] = ret as usize;
}
/*
* @param:
* TrapFrame: the Trapframe for the illegal inst exception
* @brief:
* process IllegalInstruction exception
*/
fn illegal_inst(tf: &mut TrapFrame) {
(super::BBL.illegal_insn_trap)(tf.x.as_ptr(), tf.scause.bits(), tf.sepc);
tf.sepc = mepc::read();
}
/*
* @param:
* TrapFrame: the Trapframe for the page fault exception
* @brief:
* process page fault exception
*/
fn page_fault(tf: &mut TrapFrame) {
let addr = tf.stval;
trace!("\nEXCEPTION: Page Fault @ {:#x}", addr);
if !crate::memory::handle_page_fault(addr) {
crate::trap::error(tf);
}
}