Exception in MIPS
介绍
分支、跳转、异常(包括硬件中断)是三种改变控制流的事件。
同步异常是指程序执行到固定位置必定触发且每次现象一致的异常,如算术溢出异常、未定义指令异常、缺页异常等。
异步异常与当前执行程序无关,如I/O请求、内存错误、电源不稳定等。硬件中断是一种异步异常。
异常发生时,当前执行程序被中断,CPU跳转到对应的异常处理函数执行,异常处理完毕之后,继续执行被中断程序。
异常处理函数入口必须要保存被中断任务上下文,这样才能从异常返回。任务上下文包括CPU寄存器值、返回地址和栈顶指针等,通常是保存到内存中,那么就需要一个寄存器用来保存该内存段的基地址。MIPS预留了$k0和$k1两个寄存器,异常处理函数每次都会直接改写$k0和$k1寄存器值,这就意味着,正常运行的程序如果使用$k0或者$k1寄存器会导致不可预期的结果。
异常处理机制
MIPS的异常处理、MMU、Cache都是由CP0协处理器控制,CP0只能在内核态操作。
CP0寄存器使用下述指令操作:
异常处理相关CP0寄存器如下:
BadVAddr寄存器
保存异常发生时的内存地址(虚拟地址), 比如非对齐访问异常;
Cause寄存器
IP0~1表示软中断,IP2~7表示硬件中断类型,ExcCode表示异常类型,ExcCode 0表示中断。
ExcCode
Status寄存器
IM2~7为1表示使能对应中断,为0表示去使能;
IEc即当前中断模式,为1表示使能中断,为0表示禁用所有中断;
KUc即当前工作模式,为1表示User模式,为0表示Kernel模式;
IEp和KUp表示异常发生前的中断模式和工作模式;
IEo和KUo表示再之前的状态;
上述三组状态依次覆盖。
EPC寄存器
使用jal跳转指令时,发生了两件事情:
1)跳转到jal指定的地址执行
2)返回地址保存到$ra寄存器
但是当异常发生时,CPU跳转到一个固定的地址执行,即异常向量表。此时,返回地址不能保存在$ra寄存器中,因为它会覆盖$ra寄存器值,因此MIPS将异常返回地址保存在EPC寄存器中。
从异常返回
无延迟槽时
mfc0 $k0, $14 # get EPC in $k0 rfe # return from exception jr $k0 # replace PC with the return address
有延迟槽时
mfc0 $k0, $14 # get EPC in $k0 addiu $k0, 4 # make sure it points to next instruction rfe # return from exception jr $k0 # replace PC with the return address
异常处理函数
当异常发生时,CPU会进入Kernel模式,同时禁用所有中断(CPU硬件实现,软件不可控)。
禁用中断的原因是异常处理函数首先需要保存处理器状态,随后才能处理中断。具体为:
1)确定异常类型,并保存相关信息(如EPC、Cause、Status寄存器值)
2)保存异常上下文,即所有CPU寄存器值
禁用中断并不代表不处理中断,只是延迟处理。当异常处理函数中使能中断,或者异常处理函数返回时,CPU会马上处理被Pending的中断(长时间Pending会丢失中断),跳转到中断处理函数处理。
如果异常处理函数可以在很短时间内执行完毕,那么,不需要在异常处理函数中使能中断,异常返回时CPU会使能中断。这也意味着,异常处理函数中不需要保存异常上下文,如EPC、Cause、Status寄存器值,但是仍然需要保存除$k0和$k1以外的通用寄存器值。
异常嵌套
异常处理函数执行时,中断被禁用,但是不代表CPU不会被trap。当异常处理函数本身出现异常时,仍然会触发异常。
异常向量表
Reset,Soft Reset,NMI异常向量位于0xBFC0 0000。
EJTAG Debug异常向量位于0xBFC0 0480或者0xFF20 0200,取决于EJTAG_CONTROL寄存器ProbTrap位的值。
其他异常向量的地址由Vector Offset和Vector Base Address组合而来。在MIPS Release 1中,Vector Base Address是固定的;MIPS Release 2中,对于异常发生时,StatusBEV为1的异常向量,Vector Base Address由EBase寄存器指定。