Files
riscv64-kernel/src/interrupt.rs

489 lines
16 KiB
Rust

//!
//! Trap handling and syscall dispatch.
//!
//! This module contains the low-level trap handlers for machine and supervisor
//! modes and the syscall dispatch implementation used by user processes.
use alloc::{rc::Rc, str};
use io::SeekFrom;
use shared::syscall::SysCall;
use crate::{
boot::sbi::{ExtensionID, TimerFunctionID},
clear_csr,
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
process::{ExecutionContext, exit_process, sleep},
read_csr,
riscv::{disable_interrupt, dump_cpu},
scheduler::SCHEDULER,
set_csr, syscall,
time::{IRQ_M_EXTERNAL, IRQ_M_TIMER, setup_next_timer_interrupt},
virtio::input::HANDLING_INTERRUPT,
virtual_fs::{FILE_SYSTEM, VirtualFileSystem},
write_csr,
};
use core::{alloc::Layout, arch::naked_asm, cell::RefCell, time::Duration};
use crate::time::{setup_timer_interrupt, timer_interrupt};
#[unsafe(no_mangle)]
/// Machine-mode trap handler.
///
/// Handles synchronous exceptions and SBI calls that occur while running in
/// machine mode. This function decodes `mcause` and either handles the
/// condition or forwards it to a panic.
unsafe extern "C" fn machine_trap_handler(
mcause: u64,
mie: u64,
mip: u64,
interrupt_state: *const MachineInterruptState,
) {
let mepc = read_csr!(mepc);
let mtval = read_csr!(mtval);
if mcause & (1 << 63) == 0 {
let message = match mcause & !(1 << 63) {
0 => "Instruction address misaligned",
1 => "Instruction access fault",
2 => "Illegal instruction",
3 => "Breakpoint",
4 => "Load address misaligned",
5 => "Load access fault",
6 => "Store/AMO address misaligned",
7 => "Store/AMO access fault",
8 => "Environment call from U-mode",
9 => {
// Environment call from S-mode
let eid = unsafe { (*interrupt_state).a[7] };
let fid = unsafe { (*interrupt_state).a[6] };
#[allow(clippy::single_match)]
match eid {
c if c == ExtensionID::Time as usize => match fid {
c if c == TimerFunctionID::SetTimer as usize => {
clear_csr!(mip, 1 << 5);
}
_ => {}
},
_ => panic!("Unhandled SBI call eid={eid}, fid={fid}"),
}
// Advance mepc to exit the ecall
let mepc = read_csr!(mepc);
write_csr!(mepc, mepc + 4);
return;
}
11 => "Environment call from M-mode",
12 => "Instruction page fault",
13 => "Load page fault",
15 => "Store/AMO page fault",
16 => "Double trap",
18 => "Software check",
19 => "Hardware error",
_ => panic!(
"unknown exception, mcause={}, mie={}, mip={}",
mcause, mie, mip
),
};
disable_interrupt();
panic!(
"{} at PC=0x{:x}, mtval={}\n\n{}",
message,
mepc,
mtval,
dump_cpu()
);
} else {
#[allow(clippy::single_match)]
match mcause & !(1 << 63) {
7 => {
setup_next_timer_interrupt();
set_csr!(mip, 1 << 5);
}
11 => {
set_csr!(mip, 1 << 9);
}
_ => {}
}
}
}
#[unsafe(no_mangle)]
/// Supervisor-mode trap handler and syscall dispatcher.
///
/// Handles exceptions and interrupts coming from supervisor mode, performs
/// syscall decoding, and invokes the scheduler when needed.
unsafe extern "C" fn supervisor_trap_handler(
mut interrupt_state: *mut ExecutionContext,
scause: u64,
_sie: u64,
_sip: u64,
) -> *const ExecutionContext {
if scause & (1 << 63) == 0 {
#[allow(clippy::single_match)]
match scause & !(1 << 63) {
8 => {
// Advance sepc to exit the ecall
unsafe {
(*interrupt_state).mepc = (*interrupt_state).mepc.byte_add(4);
}
// Get back to run the syscall again
fn loop_syscall(interrupt_state: *mut ExecutionContext) {
unsafe {
(*interrupt_state).mepc = (*interrupt_state).mepc.byte_sub(4);
}
}
// Environment call from S-mode
let syscall_u64: u64 = unsafe { (*interrupt_state).a[0] };
let a1: u64 = unsafe { (*interrupt_state).a[1] };
let a2: u64 = unsafe { (*interrupt_state).a[2] };
let a3: u64 = unsafe { (*interrupt_state).a[3] };
let a4: u64 = unsafe { (*interrupt_state).a[4] };
let syscall: SysCall = syscall_u64.into();
match syscall {
SysCall::Open => {
let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
let virtual_node = Rc::new(RefCell::new(unsafe {
FILE_SYSTEM.open(path.as_ref()).unwrap()
}));
let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process_mut();
let fd = if let Some(fd) =
current_process.fd_table.iter().position(Option::is_none)
{
current_process.fd_table[fd] = Some(virtual_node);
fd
} else {
let fd = current_process.fd_table.len();
current_process.fd_table.push(Some(virtual_node));
fd
};
unsafe { (*interrupt_state).a[0] = fd as u64 };
}
SysCall::Close => {
let fd = a1;
let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process_mut();
let vnode = current_process.fd_table[fd as usize].take().unwrap();
vnode.borrow_mut().close();
}
SysCall::Write => {
let fd = a1;
let buf =
unsafe { core::slice::from_raw_parts(a2 as *const u8, a3 as usize) };
let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process_mut();
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
let res = vnode.borrow_mut().write(buf).unwrap();
unsafe { (*interrupt_state).a[0] = res as u64 };
}
SysCall::Read => {
let fd = a1;
let buf =
unsafe { core::slice::from_raw_parts_mut(a2 as *mut u8, a3 as usize) };
let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process_mut();
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
let res = vnode.borrow_mut().read(buf).unwrap();
if res == 0 && !buf.is_empty() {
loop_syscall(interrupt_state);
scheduler.schedule(&mut interrupt_state);
} else {
unsafe { (*interrupt_state).a[0] = res as u64 };
}
}
SysCall::Seek => {
let fd = a1;
let seek = match a2 {
0 => SeekFrom::Start(a3),
1 => SeekFrom::End(a3 as i64),
2 => SeekFrom::Current(a3 as i64),
_ => unimplemented!(),
};
let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process_mut();
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
vnode.borrow_mut().seek(seek).unwrap();
}
SysCall::Spawn => {
let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
let argc = a3 as isize;
let argv = a4 as *const *const u8;
let mut scheduler = SCHEDULER.lock();
let current_process = scheduler.get_current_process();
let fd = current_process.fd_table.clone();
let new_process = scheduler.create_process_from_file(path, argc, argv);
new_process.fd_table = fd;
unsafe { (*interrupt_state).a[0] = new_process.pid as u64 };
}
SysCall::ExecVE => {
// let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
// let mut scheduler = SCHEDULER.lock();
// scheduler.create_process_from_file(path, &[]);
unimplemented!("ExecVE is not implemented")
}
SysCall::WaitPid => {
let pid = a1;
let mut scheduler = SCHEDULER.lock();
let alive = scheduler.is_process_alive(pid);
if alive {
loop_syscall(interrupt_state);
scheduler.schedule(&mut interrupt_state);
}
}
SysCall::Alloc => {
let layout = Layout::from_size_align(a1 as usize, a2 as usize).unwrap();
// Allocate memory and put the pointer in a0
unsafe { (*interrupt_state).a[0] = syscall::alloc(layout) as u64 };
}
SysCall::Dealloc => {
let ptr = a1 as *mut u8;
let layout = Layout::from_size_align(a2 as usize, a3 as usize).unwrap();
// Free memory
unsafe { syscall::dealloc(ptr, layout) };
}
SysCall::Exit => exit_process(&mut interrupt_state),
SysCall::NanoSleep => sleep(Duration::new(a1, a2 as u32), &mut interrupt_state),
SysCall::Unimplemented => {
unimplemented!("Syscall {syscall_u64} is not implemented")
}
}
}
_ => {}
}
} else {
#[allow(clippy::single_match)]
match scause & !(1 << 63) {
5 => {
unsafe {
core::arch::asm!(
"ecall",
in("a0") 0,
in("a6") TimerFunctionID::SetTimer as u64,
in("a7") ExtensionID::Time as u64,
clobber_abi("system")
);
}
timer_interrupt();
SCHEDULER.lock().schedule(&mut interrupt_state);
}
9 => {
unsafe {
let irq = core::ptr::read_volatile((HANDLING_INTERRUPT + 0x1004) as *const u32);
if irq != 0 {
if irq == 34 {
KBD_DRIVER.handle_interrupt();
} else if irq == 35 {
MOUSE_DRIVER.handle_interrupt();
}
// Complete interrupt
core::ptr::write_volatile((HANDLING_INTERRUPT + 0x1004) as *mut u32, irq);
} else {
panic!()
}
}
}
_ => {}
}
}
interrupt_state
}
/// Install the machine-mode trap entry point and enable timer interrupts.
pub unsafe fn setup_machine_trap_handler() {
write_csr!(mtvec, _machine_mode_trap);
set_csr!(mie, IRQ_M_TIMER);
set_csr!(mie, IRQ_M_EXTERNAL);
}
/// Install the supervisor-mode trap entry point and configure periodic timer.
pub unsafe fn setup_supervisor_trap_handler() {
write_csr!(stvec, _supervisor_mode_trap);
set_csr!(sie, 1 << 9);
setup_timer_interrupt();
}
#[unsafe(naked)]
#[unsafe(no_mangle)]
/// Low-level machine-mode trap entry (assembly stub).
///
/// Saves the machine-mode callee-saved context, constructs a stack frame and
/// calls `machine_trap_handler` in Rust. Implemented as a naked function
/// with inline assembly.
unsafe extern "C" fn _machine_mode_trap() {
naked_asm!(
"
addi sp, sp, -128
# Store the current frame
sd ra, 120(sp)
sd a0, 0(sp)
sd a1, 8(sp)
sd a2, 16(sp)
sd a3, 24(sp)
sd a4, 32(sp)
sd a5, 40(sp)
sd a6, 48(sp)
sd a7, 56(sp)
sd t0, 64(sp)
sd t1, 72(sp)
sd t2, 80(sp)
sd t3, 88(sp)
sd t4, 96(sp)
sd t5, 104(sp)
sd t6, 112(sp)
csrr a0, mcause
csrr a1, mie
csrr a2, mip
mv a3, sp
jal machine_trap_handler
# Restore registers
ld ra, 120(sp)
ld a0, 0(sp)
ld a1, 8(sp)
ld a2, 16(sp)
ld a3, 24(sp)
ld a4, 32(sp)
ld a5, 40(sp)
ld a6, 48(sp)
ld a7, 56(sp)
ld t0, 64(sp)
ld t1, 72(sp)
ld t2, 80(sp)
ld t3, 88(sp)
ld t4, 96(sp)
ld t5, 104(sp)
ld t6, 112(sp)
addi sp, sp, 128
mret"
)
}
#[unsafe(naked)]
#[unsafe(no_mangle)]
/// Low-level supervisor-mode trap entry (assembly stub).
///
/// This stub saves the full register state and forwards control to
/// `supervisor_trap_handler` implemented in Rust.
unsafe extern "C" fn _supervisor_mode_trap() {
naked_asm!(
"
// Store sp before it gets modified
sd sp, 8-264(sp)
addi sp, sp, -264
# Store the current frame
sd ra, 0(sp)
// sp
sd gp, 16(sp)
sd tp, 24(sp)
sd a0, 32(sp)
sd a1, 40(sp)
sd a2, 48(sp)
sd a3, 56(sp)
sd a4, 64(sp)
sd a5, 72(sp)
sd a6, 80(sp)
sd a7, 88(sp)
sd t0, 96(sp)
sd t1, 104(sp)
sd t2, 112(sp)
sd t3, 120(sp)
sd t4, 128(sp)
sd t5, 136(sp)
sd t6, 144(sp)
sd s0, 152(sp)
sd s1, 160(sp)
sd s2, 168(sp)
sd s3, 176(sp)
sd s4, 184(sp)
sd s5, 192(sp)
sd s6, 200(sp)
sd s7, 208(sp)
sd s8, 216(sp)
sd s9, 224(sp)
sd s10, 232(sp)
sd s11, 240(sp)
csrr t0, sepc
sd t0, 248(sp)
csrr t0, sstatus
sd t0, 256(sp)
mv a0, sp
csrr a1, scause
csrr a2, sie
csrr a3, sip
jal supervisor_trap_handler
# Restore registers and sret
jal restore_context"
)
}
#[unsafe(naked)]
#[unsafe(no_mangle)]
/// Restore a saved execution context and perform `sret` to return to user code.
pub unsafe extern "C" fn restore_context(context: *const ExecutionContext) -> ! {
naked_asm!(
"
ld t0, 248(a0)
csrw sepc, t0
ld t0, 256(a0)
csrw sstatus, t0
ld ra, 0(a0)
ld sp, 8(a0)
ld gp, 16(a0)
ld tp, 24(a0)
ld a1, 40(a0)
ld a2, 48(a0)
ld a3, 56(a0)
ld a4, 64(a0)
ld a5, 72(a0)
ld a6, 80(a0)
ld a7, 88(a0)
ld t0, 96(a0)
ld t1, 104(a0)
ld t2, 112(a0)
ld t3, 120(a0)
ld t4, 128(a0)
ld t5, 136(a0)
ld t6, 144(a0)
ld s0, 152(a0)
ld s1, 160(a0)
ld s2, 168(a0)
ld s3, 176(a0)
ld s4, 184(a0)
ld s5, 192(a0)
ld s6, 200(a0)
ld s7, 208(a0)
ld s8, 216(a0)
ld s9, 224(a0)
ld s10, 232(a0)
ld s11, 240(a0)
ld a0, 32(a0)
sret"
)
}
#[repr(C)]
struct MachineInterruptState {
pub a: [usize; 8],
pub t: [usize; 7],
pub ra: usize,
}