You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rCore_stu/ch4/os/src/trap/mod.rs

145 lines
5.4 KiB
Rust

mod context;
use core::arch::{asm, global_asm};
use riscv::register::{mtvec::TrapMode, scause::{self, Exception, Trap}, sie, stval, stvec};
use riscv::register::scause::Interrupt;
pub use context::TrapContext;
use crate::config::{TRAMPOLINE, TRAP_CONTEXT};
use crate::println;
use crate::syscall::syscall;
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next, TASK_MANAGER};
use crate::timer::set_next_trigger;
// 引入陷入保存寄存器需要的汇编代码
global_asm!(include_str!("trap.S"));
/// 初始化stvec 寄存器, 这个寄存器保存陷入时, 入口函数的地址
pub fn init() {
set_kernel_trap_entry();
}
// 设置riscv 允许定时器中断
pub fn enable_timer_interrupt() {
unsafe {
sie::set_stimer();
}
}
// 这个函数是 trap.S 中__alltraps 保存完所有寄存器在内核栈 之后会进入到这里
#[no_mangle]
pub fn trap_handler() -> ! {
// 已经进入内核, 如果再发生中断我们进行的设置, 目前是直接跳转到一个引发panic 的函数
println!("trap_handler......");
set_kernel_trap_entry();
// 进入到了内核态, 需要把之前的用户消耗时间统计在用户时间上
TASK_MANAGER.kernel_time_start();
// 得到当前用户应用 的 trap context, 需要调用一个函数, 因为用户应用的trap context 不再内核空间, 他是在用户空间的次高地址
let cx = TASK_MANAGER.get_current_trap_cx();
let scause = scause::read(); // trap 发生的原因
let stval = stval::read(); // trap的附加信息
println!("trap_handler......1");
// 根据发生的原因判断是那种类别
match scause.cause() {
// 用户态发出的系统调用
Trap::Exception(Exception::UserEnvCall) => {
cx.sepc += 4; // +4 是因为我们需要返回 ecall指令的下一个指令
cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize;
}
// 访问不允许的内存
Trap::Exception(Exception::StoreFault)
| Trap::Exception(Exception::StorePageFault)
| Trap::Exception(Exception::LoadFault)
| Trap::Exception(Exception::LoadPageFault) => {
println!("[kernel] PageFault in application, kernel killed it.");
exit_current_and_run_next();
}
// 非法指令
Trap::Exception(Exception::IllegalInstruction) => {
println!("[kernel] IllegalInstruction in application, kernel killed it.");
exit_current_and_run_next();
}
Trap::Interrupt(Interrupt::SupervisorTimer) => {
// 发生时钟中断之后, 继续设置下一个时钟中断的发起时间
set_next_trigger();
// 暂停当前任务, 切换新的任务
suspend_current_and_run_next();
}
// 未知错误
_ => {
panic!(
"Unsupported trap {:?}, stval = {:#x}!",
scause.cause(),
stval
);
}
}
// 即将进入用户态, 把内核使用的时间统计在内核时间上
TASK_MANAGER.user_time_start();
trap_return()
}
#[no_mangle]
/// Unimplement: traps/interrupts/exceptions from kernel mode
/// Todo: Chapter 9: I/O device
pub fn trap_from_kernel() -> ! {
panic!("a trap from kernel!");
}
#[no_mangle]
/// set the new addr of __restore asm function in TRAMPOLINE page,
/// set the reg a0 = trap_cx_ptr, reg a1 = phy addr of usr page table,
/// finally, jump to new addr of __restore asm function
pub fn trap_return() -> ! {
// 设置 用户态的中断处理函数
set_user_trap_entry();
let trap_cx_ptr = TRAP_CONTEXT; // 用户空间虚拟地址的次高页, 保存了trap context, 在返回用户空间的时候, 需要恢复trap context中保存的寄存器信息
let user_satp = TASK_MANAGER.get_current_token(); // 当前用户应用的 页表所在的地址 需要在trap.S中, 等下恢复完寄存器之后 修改的用户应用自己的页表
extern "C" {
fn __alltraps();
fn __restore();
}
// TRAMPOLINE + (__restore - __alltraps), 得到的就是用户空间中 虚拟地址跳板的位置, 增加一个 (__alltraps低地址 到 __restore高地址的偏移值), 就得到了 strampoline -> __restore 的虚拟地址
let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;
println!("restore_va: {:?}", restore_va);
println!("trap_cx_ptr :{:?}", trap_cx_ptr);
println!("user_satp :{:?}", user_satp);
unsafe {
asm!(
"fence.i", // 清空 i-cache
"jr {restore_va}", // 跳转到 strampoline -> __restore 地址
restore_va = in(reg) restore_va,
in("a0") trap_cx_ptr, // a0 = virt addr of Trap Context
in("a1") user_satp, // a1 = phy addr of usr page table
options(noreturn)
)
}
}
fn set_kernel_trap_entry() {
// 设置内核法中 trap 的处理
// 当前我们直接panic
unsafe {
println!("set_kernel_trap_entry");
stvec::write(trap_from_kernel as usize, TrapMode::Direct);
}
}
fn set_user_trap_entry() {
// 设置 用户态的中断处理函数, 为我们的跳板, 这个跳板在用户空间的虚拟地址最高页, 虚拟地址最高页被映射在了十几物理内存的trap 处理函数所在的段执行__alltraps
unsafe {
println!("set_user_trap_entry");
stvec::write(TRAMPOLINE as usize, TrapMode::Direct);
}
}