代码仓库为 hustos riscv-pke

以下内容基于 lab2 代码

入口在哪?

入口为 kernel/machine/mentry.S_mentry ,它调用了 kernel/machine/minit.cm_start(uintptr_t hartid, uintptr_t dtb) ,两个参数并没有在 _mentry 中设置,这是因为 spike 会自动设置 a0 寄存器为 CPU id ,设置 a1 寄存器为设备树字符串,这刚好也符合 RV 的传参规则。

值得注意的是, spike 模拟执行 pke 代码时是从 M 模式开始的。

_mentry 里面做了什么?

首先是给 mscratch 寄存器赋值为 0 ,再设置操作系统的堆栈。

设置操作系统堆栈的具体步骤是:将 mhartid 寄存器的值读到 a4 寄存器,之后一系列四则运算重新计算栈底寄存器,让不同 CPU 核心的堆栈不会冲突。比如 thread0 的栈底是 a , thread1 的栈底就应该是 a + 4096 。

m_start 函数里面做了什么?

初始化了一些 spike 的东西,这些不是很重要。

之后就是将保存中断时上下文的 g_itrframe 的地址赋值给了 mscratch 寄存器(也就是 _mentry 中的注释, mscratch 指向 M 模式下的栈底)。

将 MPP 设置成了 S 模式,这个 MPP(machine previous privilege) 是指进入 M 模式之前的模式,这样设置的目的是在之后的 mret 后能进入 S 模式。之后将 mepc 寄存器设置为 s_start ,这就能在之后的 mret 后不仅能进入 S 模式,还能从 s_start 开始执行。

操作系统要在 M 模式执行系统调用,所以要设置系统调用时的入口,也就是把 mtvec 寄存器赋值为 mtrapvec 。之后分析如何进入系统调用时再详细介绍。

值得注意的是 mtrapvec 要 4-byte 对齐, mtvec 寄存器虽然是 MXLEN 长度的,但是它后两位使用来标识是直接系统调用还是向量系统调用的,所以它只能存 4-byte 对齐的函数入口地址。

之后依次,打开 M 模式中断,将所有中断和异常委托到 S 模式执行,再打开 S 模式中断,初始化时钟中断。

最后调用 mret 退出 M 模式,进入到预设好的 S 模式代码,也就是 s_start

s_start 函数里面做了什么?

(未完待续)