实验一:中断
实验之前
- 阅读实验指导零和一,最好一步步跟着实现一遍。
- checkout 到仓库中的
lab-1
分支,实验题将以此展开。
我们的实验题会提供一个基础的代码框架,以便于进行实验。如果你选择参考教程,自己编写操作系统,这个代码框架也可以用来进行对照。
实验题
原理:在
rust_main
函数中,执行ebreak
命令后至函数结束前,sp
寄存器的值是怎样变化的?Click to showsp
首先减去一个Context
的大小(入栈),然后原sp
的值被保存到这个入栈的Context
中。执行
handle_interrupt
的过程中,随着局部变量的使用,编译器可能会自动加入一些出入栈操作。但无论如何,handle_interrupt
前后sp
的值是一样的。从
handle_interrupt
返回后,执行__restore
,在最后将保存的原sp
值恢复。
分析:如果去掉
rust_main
后的panic
会发生什么,为什么?Click to showrust_main
返回后,程序并没有停止。rust_main
是在entry.asm
中通过jal
指令调用的,因此其执行完后会回到entry.asm
中。但是,entry.asm
并没有在后面写任何指令,这意味着程序将接着向后执行内存中的任何指令。我们可以通过
rust-objdump -d -S os/target/riscv64imac-unknown-none-elf/debug/os | less
来查看汇编代码,其中就能看到:_start
只有短短三条指令,而后面则放着许多 Rust 库中的函数。这些指令可能导致程序进入循环,或崩溃退出。实验
如果程序访问不存在的地址,会得到
Exception::LoadFault
。模仿捕获ebreak
和时钟中断的方法,捕获LoadFault
(之后panic
即可)。Click to show直接在
match
中添加一个 arm 即可。例如Trap::Exception(Exception::LoadFault) => panic!()
在处理异常的过程中,如果程序想要非法访问的地址是
0x0
,则打印SUCCESS!
。Click to show如果程序因无效访问内存造成异常,这个访问的地址会被存放在
stval
中,而它已经被我们作为参数传入handle_interrupt
了,因此直接判断即可添加或修改少量代码,使得运行时触发这个异常,并且打印出
SUCCESS!
。- 要求:不允许添加或修改任何 unsafe 代码
Click to show- 解法 1:在
interrupt/handler.rs
的breakpoint
函数中,将context.sepc += 2
修改为context.sepc = 0
(则sret
时程序会跳转到0x0
) - 解法 2:去除
rust_main
中的panic
语句,并在entry.asm
的jal rust_main
之后,添加一行读取0x0
地址的指令(例如jr x0
或ld x1, (x0)
)