代码仓库为 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
函数里面做了什么?
(未完待续)