实验一:中断
实验之前
- 阅读实验指导零和一,最好一步步跟着实现一遍。
- 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))