0%

Risc-V的三种模式

机器模式(machine-mode)

机器模式(缩写为 M 模式,M-mode)是 RISC-V 中 hart(hardware thread,硬件线程)可以执行的最高权限模式。在 M 模式下运行的 hart 对内存,I/O 和一些对于启动和配置系统来说必要的底层功能有着完全的使用权。

机器模式可以拦截并处理异常,异常被分为两类。一类是同步异常,指执行了无效操作码的指令,或者访问了无效的内存地址。另一类即是中断(软件中断硬件中断)。

机器模式下的异常处理

八个控制状态寄存器(CSR)是机器模式下异常处理的必要部分:

  • mtvec(Machine Trap Vector)它保存发生异常时处理器需要跳转到的地址。
  • mepc(Machine Exception PC)它指向发生异常的指令。
  • mcause(Machine Exception Cause)它指示发生异常的种类。
  • mie(Machine Interrupt Enable)它指出目前哪些中断可以被处理。
  • mip(Machine Interrupt Pending)保存目前待处理的中断。
  • mtval(Machine Trap Value)它保存了陷入(trap)的附加信息:地址例外中出错的地址、发生非法指令例外的指令本身,对于其他异常,它的值为 0。
  • mscratch(Machine Scratch)它暂时存放一个字大小的数据。
  • mstatus(Machine Status)它保存全局中断开关,以及许多其他的状态,其中包括当前以哪种模式运行

处理器在 M 模式下运行时,只有在全局中断开关 mstatus.MIE=1时才会产生中断.此外,每种类型中断在CSRmie中都有自己的开关。举个例子,xv6在最开始运行时,会在start.c中完成对时钟中断器的初始化。

xv6/kernel/start.c->timerinit()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 1. 将mstatus.MIE = 1, 打开了M模式的全局中断开关
* 2. 将mie.MTIE = 1, 允许了M模式发生时钟中断异常
*/
void timerinit() {

// each CPU has a separate source of timer interrupts.
int id = r_mhartid();

// ask the CLINT for a timer interrupt.
int interval = 1000000; // cycles; about 1/10th second in qemu.
*(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval;

// prepare information in scratch[] for timervec.
// scratch[0..3] : space for timervec to save registers.
// scratch[4] : address of CLINT MTIMECMP register.
// scratch[5] : desired interval (in cycles) between timer interrupts.
// 为时钟中断的中断处理函数timervec, 提供一些配置信息
uint64 *scratch = &mscratch0[32 * id];
scratch[4] = CLINT_MTIMECMP(id);
scratch[5] = interval;
w_mscratch((uint64)scratch);

// set the machine-mode trap handler.
// 机器模式下,发生中断时跳转的中断处理函数,在M模式下,仅处理时钟中断异常,所以入口直接设置成了时钟中断处理函数的地址。
w_mtvec((uint64)timervec);
// IE->interrupt execption->中断异常
// enable machine-mode interrupts.
w_mstatus(r_mstatus() | MSTATUS_MIE);

// enable machine-mode timer interrupts.
w_mie(r_mie() | MIE_MTIE);
}

如果timerinit()初始化完成后,发生了时钟中断,那么此时寄存器mipMTIE会置为1,那么此时就会发生中断。

中断什么时候发生?当CPU执行一条指令前,他会去检查一下对应的中断寄存器,对应时钟中断,当检查到mip.MTIE = 1,即此时有一条时钟中断,且对应的mstatus.mie = 1 && mie.MTIE = 1,那么此时就会发生时钟中断。

在机器模式下,当CPU发生发生异常时,硬件会自动做出以下动作:

  1. 发生异常指令的PC保存到CSRmepc中,并将PC设置为CSRmtvec的值
  2. 根据异常来源设置CSRmcause,将mtval设置为设置为出错的地址或者其他特定的异常信息。
  3. mstatus.MIE置为0,禁用全局中断,并将mstatus.MIE的值保存到mstatus.MPIE
  4. 将异常前的运行模式保存到mstatus.MPP中,并将运行模式切换到M模式。

此时跳转到mtvec保存的地址中去执行,当返回时,需要依赖mret指令,执行mret执行将会执行上述步骤的逆操作,返回到原先发生异常的状态。

用户模式 (user-mode)

对于简单的嵌入式系统来说,机器模式就够了,而对于操作系统来说,上面运行这很多用户程序,需要保证用户程序间的内存相互隔离,以保证安全性,同时要禁止用户程序使用一些机器特权指令,例如mret等等。防止用户程序越界。

用户代码必须被限制只能访问自己那部分内存。实现了 M 和 U 模式的处理器具有一个叫做物理内存保护(PMP,Physical Memory Protection)的功能,允许 M 模式指定 U 模式可以访问的内存地址。PMP 包括几个地址寄存器(通常为 8 到 16 个)和相应的配置寄存器。这些配置寄存器可以授予或拒绝读、写和执行权限。当处于 U 模式的处理器尝试取指或执行 load 或 store 操作时,将地址和所有的 PMP 地址寄存器比较。如果地址大于等于 PMP 地址 i,但小于 PMP 地址 i+1,则 PMP i+1 的配置寄存器决定该访问是否可以继续,如果不能将会引发访问异常。

管理模式(supervisor-mode)

上一节中描述的 PMP 方案对嵌入式系统的实现很有吸引力,因为它以相对较低的成本提供了内存保护,但它的一些缺点限制了它在通用计算中的使用。由于 PMP 仅支持固定数量的内存区域,因此无法对它进行扩展从而适应复杂的应用程序。而且由于这些区域必须在物理存储中连续,因此系统可能产生存储碎片化的问题。另外,PMP 不能有效地支持对辅存的分页。

更复杂的 RISC-V 处理器用和几乎所有通用架构相同的方式处理这些问题:使用基于页面的虚拟内存。这个功能构成了监管者模式(S 模式)的核心,这是一种可选的权限模式,旨在支持现代类 Unix 操作系统,如 Linux,FreeBSD 和 Windows。S 模式比 U 模式权
限更高,但比 M 模式低。与 U 模式一样,S 模式下运行的软件不能使用 M 模式的 CSR 和指令,并且受到 PMP 的限制。本届介绍 S 模式的中断和异常,下一节将详细介绍 S 模式下的虚拟内存系统。

默认情况下,发生所有异常(不论在什么权限模式下)的时候,控制权都会被移交到M 模式的异常处理程序。但是 Unix 系统中的大多数例外都应该进行 S 模式下的系统调用。M 模式的异常处理程序可以将异常重新导向 S 模式,但这些额外的操作会减慢大多数
异常的处理速度。因此,RISC-V 提供了一种异常委托机制。通过该机制可以选择性地将中断和同步异常交给 S 模式处理,而完全绕过 M 模式。

mideleg(Machine Interrupt Delegation,机器中断委托)CSR 控制将哪些中断委托给 S模式。例如,mideleg[5]对应于 S 模式的时钟中断,如果把它置位,S 模式的时钟中断将会移交 S 模式的异常处理程序,而不是 M 模式的异常处理程序。

siesip在S模式下的作用等同于miemip在M模式下的作用。

同样的,处理中断委托外,还有同步异常委托CSR medeleg(Machine Execption Delegation)。

请注意,无论委派设置是怎样的,发生异常时控制权都不会移交给权限更低的模式。在 M 模式下发生的异常总是在 M 模式下处理。在 S 模式下发生的异常,根据具体的委派设置,可能由 M 模式或 S 模式处理,但永远不会由 U 模式处理。

S 模式有几个异常处理 CSR:sepc、stvec、scause、sscratch、stval 和 sstatus,它们 M 模式 CSR 相同的功能。监管者异常返回指令 sret 与 mret 的行为相同,但它作用于 S 模式的异常处理 CSR,而不是 M 模式的 CSR。

在管理模式下,当CPU发生发生异常时,硬件会自动做出以下动作:

  1. 发生异常指令的PC保存到CSRsepc中,并将PC设置为CSRstvec的值
  2. 根据异常来源设置CSRscause,将mtval设置为设置为出错的地址或者其他特定的异常信息。
  3. mstatus.SIE置为0,禁用全局中断,并将mstatus.SIE的值保存到mstatus.SPIE
  4. 将异常前的运行模式保存到mstatus.SPP中,并将运行模式切换到S模式。
-------- 本文结束 感谢阅读 --------