实验一:中断

优质
小牛编辑
128浏览
2023-12-01

实验一:中断

实验之前

  • 阅读实验指导零和一,最好一步步跟着实现一遍。
  • checkout 到仓库中的 lab-1 分支,实验题将以此展开。

我们的实验题会提供一个基础的代码框架,以便于进行实验。如果你选择参考教程,自己编写操作系统,这个代码框架也可以用来进行对照。

实验题

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

    Click to show
    • sp 首先减去一个 Context 的大小(入栈),然后原 sp 的值被保存到这个入栈的 Context 中。

    • 执行 handle_interrupt 的过程中,随着局部变量的使用,编译器可能会自动加入一些出入栈操作。但无论如何,handle_interrupt 前后 sp 的值是一样的。

    • handle_interrupt 返回后,执行 __restore,在最后将保存的原 sp 值恢复。

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

    Click to show

    rust_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 库中的函数。这些指令可能导致程序进入循环,或崩溃退出。

  3. 实验

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

      Click to show

      直接在 match 中添加一个 arm 即可。例如 Trap::Exception(Exception::LoadFault) => panic!()

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

      Click to show

      如果程序因无效访问内存造成异常,这个访问的地址会被存放在 stval 中,而它已经被我们作为参数传入 handle_interrupt 了,因此直接判断即可

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

      • 要求:不允许添加或修改任何 unsafe 代码
      Click to show
      • 解法 1:在 interrupt/handler.rsbreakpoint 函数中,将 context.sepc += 2 修改为 context.sepc = 0(则 sret 时程序会跳转到 0x0
      • 解法 2:去除 rust_main 中的 panic 语句,并在 entry.asmjal rust_main 之后,添加一行读取 0x0 地址的指令(例如 jr x0ld x1, (x0)