代码仓库为 hustos riscv-pke
以下内容基于 lab2 代码
入口在哪?
入口为 kernel/machine/mentry.S 的 _mentry ,它调用了 kernel/machine/minit.c 的 m_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 函数里面做了什么?
(未完待续)