实验一: 中断

实验题:

1.原理:在rust_main函数中,执行ebreak命令后至函数结束前,sp寄存器的值是怎样变化的?

在rust_main函数中执行ebreak进入中断,首先需要在栈上开辟Context所需空间,即将栈顶(即sp寄存器)减去 34*8 bits。随后在__restore中恢复了sp寄存器中位置。

2.分析:如果去掉 rust_main 后的 panic 会发生什么,为什么?

去掉rust_main后的panic后程序将不会立刻返回。执行ebreak函数后进入到interrupt_handler函数中,判断为断点后进入breakpoint函数进行处理,在breakpoint函数中打印出断点位置并将sepc寄存器+2后返回,调用__restore函数恢复所有寄存器,并跳转至Contextsepc的位置上,即会回到entry.asm中,此时entry.asm函数后并未做任何处理。

3.实验:

I. 如果程序访问不存在的地址,会得到 Exception::LoadFault。模仿捕获 ebreak 和时钟中断的方法,捕获 LoadFault(之后 panic 即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
pub fn handle_interrupt(context: &mut Context, scause: Scause, stval: usize) {
// panic!("Interrupted: {:?}", scause.cause());
match scause.cause() {
// breakpoint interrupt(ebark)
Trap::Exception(Exception::Breakpoint) => breakpoint(context),
// clock interrupt
Trap::Interrupt(Interrupt::SupervisorTimer) => supervisor_timer(context),
// Visit a non-existent address
Trap::Exception(Exception::LoadFault) => load_falut(context)
// Other Situations: Stop current thread
_ => fault(context, scause, stval),
};
}

match中加入对Exception::LoadFault异常的处理。在load_falut函数中直接panic!

II. 在处理异常的过程中,如果程序想要非法访问的地址是 0x0,则打印 SUCCESS!

答:

1
2
3
4
5
6
7
8
9
10
11
12
// Handler Load Fault
fn load_falut(context: &mut Context, scause: Scause, stval: usize) {
if (stval == 0x0){
println!("Success!")
}
panic!(
"Interrupt: {:?}\n{:x?}\nstval: {:x}",
scause.cause(),
context,
stval
)
}

III. 添加或修改少量代码,使得运行时触发这个异常,并且打印出 SUCCESS!

  • 要求:不允许添加或修改任何 unsafe 代码

handle_interript函数下加入context.sepc = 0;将触发中断指令地址设置为0,即可跳转到加载地址异常。