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.

162 lines
4.0 KiB

use mips::interrupts;
use mips::tlb;
use mips::registers::cp0;
use crate::drivers::DRIVERS;
use mips::paging::{PageTable as MIPSPageTable, PageTableEntry, PageTableFlags as EF, TwoLevelPageTable};
use mips::addr::*;
pub use self::context::*;
use crate::arch::paging::get_root_page_table_ptr;
use log::*;
#[path = "context.rs"]
mod context;
/// Initialize interrupt
pub fn init() {
extern {
fn trap_entry();
}
unsafe {
// Set the exception vector address
cp0::ebase::write_u32(trap_entry as u32);
println!("Set ebase = {:x}", trap_entry as u32);
let mut status = cp0::status::read();
// Enable IPI
status.enable_soft_int0();
status.enable_soft_int1();
// Enable clock interrupt
status.enable_hard_int5();
cp0::status::write(status);
}
info!("interrupt: init end");
}
/// Enable interrupt
#[inline]
pub unsafe fn enable() {
interrupts::enable();
}
/// Disable interrupt and return current interrupt status
#[inline]
pub unsafe fn disable_and_store() -> usize {
let e = cp0::status::read_u32() & 1;
interrupts::disable();
e as usize
}
/// Enable interrupt if `flags` != 0
#[inline]
pub unsafe fn restore(flags: usize) {
if flags != 0 {
enable();
}
}
/// Dispatch and handle interrupt.
///
/// This function is called from `trap.asm`.
#[no_mangle]
pub extern fn rust_trap(tf: &mut TrapFrame) {
use cp0::cause::{Exception as E};
trace!("Exception @ CPU{}: {:?} ", 0, tf.cause.cause());
match tf.cause.cause() {
E::Interrupt => interrupt_dispatcher(tf),
E::Syscall => syscall(tf),
E::TLBModification => page_fault(tf),
E::TLBLoadMiss => page_fault(tf),
E::TLBStoreMiss => page_fault(tf),
_ => crate::trap::error(tf),
}
trace!("Interrupt end");
}
fn interrupt_dispatcher(tf: &mut TrapFrame) {
let pint = tf.cause.pending_interrupt();
trace!(" Interrupt {:?} ", tf.cause.pending_interrupt());
if (pint & 0b100_000_00) != 0 {
timer();
} else if (pint & 0b011_111_00) != 0 {
external();
} else {
ipi();
}
}
fn external() {
// TODO
// 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 {
// TODO
for driver in DRIVERS.read().iter() {
if driver.try_handle_interrupt(None) == true {
return true
}
}
return false
}
fn ipi() {
debug!("IPI");
cp0::cause::reset_soft_int0();
cp0::cause::reset_soft_int1();
}
fn timer() {
super::timer::set_next();
crate::trap::timer();
}
fn syscall(tf: &mut TrapFrame) {
tf.epc += 4; // Must before syscall, because of fork.
let arguments = [tf.a0, tf.a1, tf.a2, tf.a3, tf.t0, tf.t1];
trace!("MIPS syscall {} invoked with {:?}", tf.v0, arguments);
let ret = crate::syscall::syscall(tf.v0, arguments, tf);
let ret = tf.v0 as isize;
// comply with mips n32 abi, always return a positive value
// https://git.musl-libc.org/cgit/musl/tree/arch/mipsn32/syscall_arch.h
if (ret < 0) {
tf.v0 = (-ret) as usize;
tf.a3 = 1;
}
}
fn page_fault(tf: &mut TrapFrame) {
// TODO: set access/dirty bit
let addr = tf.vaddr;
trace!("\nEXCEPTION: Page Fault @ {:#x}", addr);
let virt_addr = VirtAddr::new(addr);
let root_table = unsafe {
&mut *(get_root_page_table_ptr() as *mut MIPSPageTable)
};
let tlb_result = root_table.lookup(addr);
match tlb_result {
Ok(tlb_entry) => tlb::write_tlb_random(tlb_entry),
Err(()) => if !crate::memory::handle_page_fault(addr) {
crate::trap::error(tf);
}
}
}