zMIPS体系结构采用的是 精确异常
处理模式
这是什么意思呢?下面来看从“See MIPS Run”一书中的摘录:
“In a precise-exception CPU, on any exception we get pointed at one instruction(the exception victim). All instructions preceding the exception victim in execution sequence are complete; any work done on the victim and on any subsequent instructions (BNN NOTE: pipeline effects) has no side effects that the software need worry about. The software that handles exceptions can ignore all the timing effects of the CPU’s implementations”
上面的意思其实很简单:在发生这个异常之前的一切计算行为会完整的结束并体现效果。 在发生这个异常之后的一切计算行为(包含当前这条指令)将不会产生任何效果。
另外一种解释是:
A precise exception is one in which the EPC (CP0, Register 14, Select 0) can be used to identify the instruction that caused the exception. For imprecise exceptions, the instruction that caused the exception cannot be identified. Most exceptions are precise. Bus error exceptions may be imprecise.
异常处理的一般过程
With the exception of Reset, Soft Reset, NMI, and Debug exceptions, which have their own special processing as described below, exceptions have the same basic processing flow:
• If the EXL bit in the Status register
is cleared, theEPC register is loaded with the PC at which execution will be restarted and the BD bit is set appropriately in theCause register
. If the instruction is not in the delay slot of a branch, the BD bit inCausewill be cleared and the value loaded into theEPCregister is the current PC. If the instruction is in the delay slot of a branch, the BD bit inCauseis set andEPCis loaded with PC-4.If the EXL bit in theStatus register is set, theEPCregister is not loaded and the BD bit is not changed in theCauseregister.
• The CE
and ExcCode
fields of the Cause registers are loaded with the values appropriate to the exception. The CE field is loaded, but not defined, for any exception type other than a coprocessor unusable exception.
• The EXL bit is set in the Status register.
• The processor is started at the exception vector.
The value loaded into EPC represents the restart address for the exception and need not be modified by exception handler software in the normal case. Software need not look at the BD bit in the Cause register unless is wishes to identify the address of the instruction that actually caused the exception. Note that individual exception types may load additional information into other registers. This is noted in the description of each exception type below.
EPC
中存放的是异常发生时执行的指令地址,或者分支延时发生异常,则存放的是分支的指令地址,不管怎么样,异常处理函数返回都从EPC开始恢复执行
,如果在分支延时指令发生异常
,则需要在cause寄存器中存放相应标志
,这样就可以准确的知道发生异常的指令地址了。
Operation:
1 | ifStatus EXL= 0 then |
As with any procedure, the exception handler must save any registers it may modify, and then restore them before returning control to the interrupted program. Saving registers in memory poses a problem in MIPS:
addressing the memory requires a register (the base register) in which the address is formed. This means that a register must be modified before any register can be saved! The MIPS register usage convention (see Laboratory 4) reserves registers $26 and $27( $k0
and$k1
) for the use of the interrupt handler. This means that the interrupt handler can use these registers without having to save them first. A user program that uses these registers may find them unexpectedly changed.
The CPU operates in one of the two possible modes,userandkernel.User programs run in user mode. The CPU enters the kernel mode when an exception happens
. Coprocessor 0 can only be used in kernel mode.
说明:为何分支延时槽中的指令发生异常要从分支指令重新
执行呢,这是因为mips的指令执行是流水线结构,分析指令的执行结果不会影响到延时槽中指令的执行,也就是说不管分支指令往哪里跳,延时槽的指令都会执行
,如果EPC保存延时指令地址,则分析指令执行的结果将会丢失,这样异常处理结束后恢复执行的结果就不正确
异常入口(向量)
The Reset
,Soft Reset
, andNMI exceptions
are always vectored to location 0xBFC0_0000
. Debug exceptions are vectored to location 0xBFC0_0480
or to location 0xFF20_0200
if the ProbTrap bit is 0 or 1, respectively, in the EJTAG Control register
(ECR).
如果在’ EJTAG控制寄存器’ (ECR)中
ProbTrap
位分别为0或1,则调试异常被指向位置’ 0xBFC0_0480 ‘或位置’ 0xFF20_0200 ‘。
Addresses forall other exceptions
are a combination of a vector offset and a base address .
Table4-2 gives the base address as a function of the exception and whether the BEV bit is set in theStatus register.
Table 4-3 gives the offsets from the base address as a function of the exception.
CauseIV: 将CP0 CauseIV
位设置为1
会导致中断异常使用专用的异常向量偏移量(0x200), 而不是使用一般的异常向量偏移量(0x180)。
Table 4-4combines these two tables into one that contains all possible vector addresses as a function of the state that can affect the vector selection.
将这两个表合并为一个表,其中包含所有可能的向量地址,作为可能影响向量选择的状态函数。
In MIPS32® Release 2 and higher architectures, software is allowed to specify the vector base address via the CP0 Ebase
register for exceptions that occur when CP0 Status BEV equals 0.
- StatusBEV= 1: Exceptions vector to an uncached entry point in KSEG1: 0xBFC00xxx
- StatusBEV= 0: Exceptions vector to cached entry points in KSEG0:
defined by CP0 Ebase register
, plus someoffset
Note:StatusBEV = 1 at reset. IfEbaseis to be changed, it must be done with StatusBEV= 1(i.e. at system boot). The operation of the CPU isUNDEFINEDifEbaseis written whenStatusBEV= 0. TheEbasedefault is 0x8000_0000 after reset.
EBase寄存器是一个可读写寄存器,包含例外向量基地址和一个只读的CPU号。
对Cache Error
这个特殊的异常来说,需要给他安排一个任何时候都是Uncached的基地址了。因为发生这个异常时Cache已经不可靠了,在处理它是就不能使用它了。
因此这个异常的入口基地址为:
BEV = 1 : BFC0,0300 (系统启动地址空间 : kseg1)
BEV = 0 :[SP]: A000,0000 (物理内存地址 : kseg1)
[MP]: EBASE[31.30] || 1 || EBASE[28…12] || 0x000 (物理内存地址 : kseg1)
上面的总结一下: Reset ,Soft Reset和NMI
: 不受任何配置的影响,异常向量位置总是在0XBFC0_0000
General Exception:异常向量在0xBFC0_0200 + 0x180
或 Ebase + 0x180
Interrupt: IV 表示是否使用专用的异常处理向量, IV=0,采用General Exception中断向量, IV=1,则采用int专用的中断向量
TLB refill: EXL为0时,采用TLB refill专用的异常处理向量,EXL为1时,采用General Exception中断向量
异常优先级
所谓的优先级是指:当在某个时刻,同时多个异常或中断出现时,CPU将会按照上述的优先级来处理。
前面一列为exception的编号,后面一列为改异常的描述
异常相关寄存器
- The BadVAddr register
This register (its name stands for Bad Virtual Address) will contain the memory address where the exception has occurred.
An unaligned memory access, for instance, will generate an exception and the address where the access was attempted
will be stored in BadVAddr.
SR(Status Register,状态寄存器)
- EXL Exception Level ; set by the processor when any exception other than Reset, Soft Reset, NMI, or Cache Error exception are taken.
0: normal 1: exception
当EXL被置位时, 中断是被禁止的。 换句话说,这时SR[IE]位是不管用了,相当于所有的中断都被屏蔽了
。
- TLB Refill异常将会使用
General Exception Vector
而不是缺省的TLB Refill Vector. - 如果再次发生异常,
EPC将不会被自动更新
。这一点要非常注意。如果想支持嵌套异常,要在异常处理例程中清EXL位。当然要先保存EPC的值。另外要注意的:MIPS当陷 入Exception/Interrupt时,并不改变SR[UX],SR[KX]或SR[SX]的值。SR[EXL]为1自动的将CPU mode运行在核心模式下。这一点要注意。
- ERL Error Level ; set by the processor when
Reset, Soft Reset, NMI, or Cache Error exception
are taken.
0: normal 1: error
当ERL被置位时,中断被禁止. 中断返回ERET
使用的是ErrorEPC
而不是EPC。需要非常注意这个区别。
Kuseg和xkuseg 被认为是没有映射(Mapped)的和没有缓存(Un-Cached)。
可以这样理解,MIPS CPU只有在这个时刻才是一种实模式(real mode),可以不需要TLB的映射, 就直接使用kuseg
的地址空间。
The ERET instruction to return from exception is used for returning from exception level (Status.EXL) and error level (Status.ERL). If both bits are set however we should be returning from ERL first, as ERL can interrupt EXL, for example when an NMI is taken.
都是通过eret返回的,如果EXL和ERL同时设置了,则应该首先从ERL返回
,PC设置为ErrorPC, 清除ERL,注意这时不会清除EXL。
ERET指令用模拟器实现的代码大致如下:
1 | if (kvm_read_c0_guest_status(cop0) & ST0_ERL) { |
IE Interrupt Enable 0: disable interrupts 1: enable interrupts。请记住: 当SR[EXL]或SR[ERL]被SET时,SR[
IE
]是无效的。BEV Normal/Bootstrap exception vectors location
BEV=1 :非缓存异常处理入口固定定位于非缓存的、启动安全的 kseg1 内存区域
BEV=0 :异常处理入口不固定,通过 EBase 寄存器可以编程移动,系统正常运行时为 0, 由EBASE 指定
SR Soft Reset, 如果是soft reset,该位置1,表明是软件复位
NMI 如果是NMI,该位置1,表明是不可屏蔽中断
IM[7:0] Interrupt Mask
UM Kernel/User Mode, UM=1用户模式,中断发生时不改变该bit的值
UM:ERL:EXL Mode
100: User
000: Kernel
x10: Kernel (exception handling)
x01: Kernel (error handling)
Cause
在处理器异常发生时, 这个寄存器标识了异常的原因。其中最重要的是2-6位,5个bit的exception code位。它们标识了引起异常的原因,具体数值代表的异常类型
BD: Exception happened in a branch delay slot
IV:
Use general vs special interrupt vector
打开iv 后, interrupt 走0x200的vector offset(vector base 为0xbfc00200或者ebase)IP[7:0]: Interrupt(s) pending
Exc Code: Exception code
EPC存放返回地址
这个寄存器的作用很简单, 就是保存异常发生时的指令地址。从这个地方可以找到异常发生的指令,再结合BadVaddr
sp
等寄存器,就可以推导出异常时的程序调用关系,从而定位问题的根因
WatchLo、WatchHi
这一对寄存器用来设定内存硬件断点
,也就是对指定点的内存进行检测。当访问的内存地址和这两个寄存器地址一致时,会发生一个异常。 应该是不同于gdb的watch指令,后面可以试一下。
CP0相关
CP0 主要操作
mfc0 rt,rd 将cp0的rd寄存器内容传输到rt通用寄存器
mtc0 rt, rd 将rd通用寄存器的内容传输到CP0中寄存器rd
mfhi/mflo rt 将CP0 的hi、lo 寄存器的内容传输到rt通用寄存器中
mthi/mtlo rt 将rt通用寄存器的内容传输到CP0的hi、lo 寄存器中
CP0 冒险现象
mips体系结构是一个无互锁,高度流水的五级pipeline架构,这就意味着,前一条指令如果尚未执行完,后一条指令可能已经进入了取指令、译码阶段。这样可能发生CP0 冒险(CP0 Hazard)现象。mfc和mtc的指令执行速度是比较慢的,开始执行完下一条指令时, 有可能CP0寄存器的值尚未最后传输到指定的目标通用寄存器中。此时,如果读取该通用寄存器,有可能未能得到正确的值。这就是所谓的CP0 冒险现象。
为避免CP0 冒险, 我们在编程时需要在CP0 操作指令的后面加入一条与前一条指令的目的通用寄存器无关的指令,也就是所谓的延迟槽(delay slot), 如果对性能不敏感,可以考虑用nop填充延迟槽
MIPS cpu中断机制
mips 异常
在mips中,中断 陷阱trap 系统调用和任何可以中断程序正常执行流的情况都称之为异常
精确异常: 在引发异常的指令执行时,后面一条指令已经完成了读取和译码的预备工作,当异常产生时,这些预备工作被废弃
,CPU从异常中返回时,再重新做读取和译码的工作。
Mips 对异常的处理是给异常分配一些类型,然后由软件给它们定义一些优先级,然后由同一个入口
进入异常分配程序,在分配程序中根据类型及优先级
确定该执行哪个对应的函数
CP0中的epc寄存器用于指向异常发生时指令跳转前的执行位置,一般是被中断指令地址。当异常时,是返回这个地址继续执行,但如果被中断指令是在分支延迟槽中,则会硬件自动处理使epc往回指一条指令(pc-4),即分支指令。在重新执行分支指令时,分支延迟槽中的指令会被再执行一次。
mips异常处理步骤
- 设置epc,指向返回位置
- 设置status寄存器,exl位迫使cpu进入内核模式,并且
禁用中断
,即exl位置1 - 设置cause 寄存器, 使得软件能看见异常原因, 地址异常时,也要设置
BadVAddr
寄存器,存储管理系统异常还要设置一些mmu寄存器 - cpu从异常入口点取指令执行
mips异常处理例子
- 保护现场:在异常处理例程入口,需要保护被中断的程序的现场,存储寄存器的状态,保证关键状态不被覆盖
- 处理异常: 根据cause exccode确定发生了什么类型的异常,完成想要做的任何事情
- 准备返回: 恢复线程,修改SR,设置成安全模式(内核态,禁止异常)exl置1,也就是异常发生后的模式
- 从异常返回:指令
eret
, 即清除SR:exl位, exl置0, 将控制权返回给存储在epc中的地址
中断寄存器相关
cpu核外部
的事件,即从一些真正的硬件连线
过来的输入信号(外中断或硬中断)
Cause:exccode编码为00000中断,这些中断使cpu转向某外部事件,mips cpu有8个独立终端位(在Cause: IP7-2 和 IP 1-0段), 其中6个(IP7-2)为外部中断,2个(IP 1-0)为内部中断(可用软件访问), 片上的时钟计数/定时器都会连接到一个硬件中断位上。
mips 对中断的支持涉及到 SR 和 Cause 寄存器
使能全局中断(IE: interupt enable): 要想使能中断,SR的IE位必须置1
中断使能屏蔽(IM: interupt mask) SR[15-8]为中断屏蔽位,对应IM[7-0], 这8个bit决定了
哪些中断源有请求时可以触发一个异常。实际上是对中断信号的使能开关。
8个中断源中的6个 IM[7-2]对应cause IP[7-2], 用于外部设备硬件中断,其余2个IM[1-0]对应cause ip[1-0], 为软件中断屏蔽位,所谓中断源就是产生硬中断信号的PIC外接设备或软中断。异常级别(EXL : exception level):异常发生后, cpu立即设置SR EXL置1, 进入异常模式, 异常模式强制cpu进入内核特权级模式并屏蔽中断, 而不会理会SR其他位的值。 EXL位在已经设置的情况下,还没有真正准备好调用主内核的例程(中断恢复或者中断钩子的处理?)。在这种状态下,系统不能处理其他异常,
保持EXL 足够的时间保存现场,使软件决定cpu新的特权级别和中断屏蔽位应该如何设置
异常类型(EXCcode: Exception Code) : PIC每个输入引脚上的有效(相应的IM位 1, 未被屏蔽), 输入每个周期都会采样, 如果使能,则引起一个异常。异常处理程序检查到Cause (exccode =0)说明发生的异常是
中断
, 此时进入通用中断程序中断请求寄存器(IP: interupt pending): Cause[15-8]位为中断挂起状态位,用于指示哪些设备发生了中断,具体来说是识别PIC的哪个接入引脚对应的设备发来了中断信号,IP[7-2]随着CPU 硬件输入引脚上的信号而变化,而IP1-0为软件中断位,可读可写并存储最后写入的值。当SR:IM[7-0]某些位使能,且硬中断或软中断触发时,cause:IP[7-2]上的pending位,确定是哪个设备发生了中断
中断处理步骤:
注意和异常的区别, 异常很有可能是不可恢复异常,中断处理要麻烦一些,因为要恢复现场。
中断是异常的一种,所以中断处理只是异常处理的一条分流,经过上一层异常处理处理后,处理步骤如下:
- 将cause:IP与SR:IM进行逻辑与运算,获得一个或多个使能的中断请求
- 选择一个使能的中断来处理,优先处理最高优先级的中断
- 存储SR: IM中的中断屏蔽位,改变SR:IM 以保证
禁止当前中断
以及所有优先级小于等于本中断的中断在处理时发生 对于嵌套异常, 此时需要保护现场
- 修改cpu到合适的状态以适应中断处理程序的高层部分,这时通常允许一些嵌套中断或异常。设置全局中断使能SR:IE位,以允许处理高优先级的中断。还需要改变cpu特权级域(SR:
KSU
),使CPU处于内核态,清除SR:EXL离开异常模式,并把这些改动反映到状态寄存器中 - 执行中断处理程序,完成要做的事情
- 恢复现场,恢复相关寄存器, 返回被中断的指令(返回被中断程序)
常见的异常处理
Reset Exception
A reset exception occurs when the SI_ColdReset
signal is asserted to the processor. This exception is not maskable. When a Reset exception occurs, the processor performs a full reset initialization , including aborting state machines, establishing critical state, and generally placing the processor in a state in which it can execute instructions from uncached, unmapped address space
. On a Reset exception, the state of the processor in not defined, with the following exceptions:
对于Reset和Soft Reset异常来说,当发生此类异常时,ErrorEPC
是否准确记录了发生异常时执行的指令地址,这需要根据特定处理器的手册决定。它依赖于具体处理器的实现。而且,似乎在Reset时也没有必要去记录处理器正在执行的指令
The Random register is initialized to the number of TLB entries - 1 (4Kc core).
The Wired register is initialized to zero (4Kc core).
The Config register is initialized with its boot state.
The RP, BEV, TS, SR, NMI, and ERL fields of the Status register are initialized to a specified state.
The I, R, and W fields of the WatchLo register are initialized to 0.
The ErrorEPC register is loaded with
pc-4
if the state of the processor indicates that it was executing an instruction in thedelay slot of a branch.
Otherwise, the ErrorEPC register is loaded with pc. Note that this value may or may not be predictable.PC is loaded with
0xBFC0_0000
Cause Register ExcCode Value: None
Additional State Saved: None
Entry Vector Used: Reset (0xBFC0_0000)
Operation:
1 | Random <- TLBEntries - 1 |
Non-Maskable Interrupt (NMI) Exception
A non-maskable interrupt exception occurs when the SI_NMI
signal is asserted to the processor. SI_NMI is an edge sensitive signal - only one NMI exception will be taken each time it is asserted
. An NMI exception occurs only at instruction boundaries, so it does not cause any reset or other hardware initialization. The state of the cache, memory, and other processor states are consistent and all registers are preserved , with the following exceptions:
The BEV, TS, SR, NMI, and ERL fields of the Status register are initialized to a specified state.
The ErrorEPC register is loaded with PC-4 if the state of the processor indicates that it was executing an instruction in the delay slot of a branch. Otherwise, the ErrorEPC register is loaded with PC.
PC is loaded with 0xBFC0_0000.
Cause Register ExcCode Value: None
Additional State Saved: None
Entry Vector Used: Reset (0xBFC0_0000)
Operation:
1 | Status BEV <- 1 |
Machine Check Exception (4Kc core)
A machine check exception occurs when the processor detects an internal inconsistency
. The following condition causes a machine check exception;
Cause Register ExcCode Value: MCheck
Additional State Saved: None
Entry Vector Used: General exception vector
(offset 0x180)
Interrupt Exception
外部中断。它是唯一一个异步发生的异常。之所以说中断是异步发生的,是因为相对于其他异常来说,从时序上看,中断的发生是不可预料的,无法确定中断的发生是在流水线的哪一个阶段。MIPS的五级流水线设计如下:
IF, RD, ALU, MEM, WB。MIPS处理器的中断控制部分有这样的设计:在中断发生时,如果该指令已经完成了MEM阶段的操作,则保证该指令执行完毕。反之,则丢弃流水线对这条指令的工作。除NMI外,所有的内部或外部硬件中断(Hardware Interrupt)均共用这一个异常向量(Exception Vector)。前面提到的CP0中的Counter/Compare这一对计数寄存器,当Counter计数值和Compare门限值相等时,即触发一个硬件中断。
The interrupt exception occurs when one or more of the eight interrupt requests is enabled by the Status register and the interrupt input is asserted. The delay from assertion of an unmasked interrupt to fetch of the first instructions at the exception vector is a minimum of 5 clock cycles. More may be needed if a committed instruction has to complete before the exception can be taken. A SYNC instruction which has already started flushing the cache and write buffers must wait until this is completed before the interrupt exception can be taken.
- Register ExcCode Value: Int
- Additional State Saved:
- Entry Vector Used:
General exception vector (offset 0x180
) if the IV bit in the Cause register is 0; interrupt vector (offset 0x200
) if the IV bit in the Cause register is 1.
TLB Refill Exception
Instruction Fetch or Data Access (4Kc core)
TLB Miss Load/Write,如果试图访问没有在MMU的TLB中映射的内存地址
,会触发这个异常。在支持虚拟内存的操作系统中,这会触发内存的页面倒换,系统的Exception Handler会将所需要的内存页从虚拟内存中调入物理内存,并更新相应的TLB表项。
During an instruction fetch or data access, a TLB refill exception occurs when no TLB entry in a TLB-based MMU matches a reference to a mapped address space and the EXL bit is 0 in the Status register. Note that this is distinct from the case in which an entry matches but has the valid bit off. In that case, a TLB Invalid exception occurs.
*Cause* Register ExcCode Value:
TLBL: Reference was a load or an instruction fetch
TLBS: Reference was a store
Additional State Saved:
Entry Vector Used:
TLB refill vector (offset 0x000
) if Status EXL = 0 at the time of exception; general exception vector (offset 0x180
) if Status EXL = 1 at the time of exception
TLB Invalid Exception — Instruction Fetch or Data Access (4Kc core)
During an instruction fetch or data access, a TLB invalid exception occurs in one of the following cases:
• No TLB entry in a TLB-based MMU matches a reference to a mapped address space; and the EXL bit is 1
in theStatus register.
• A TLB entry in a TLB-based MMU matches a reference to a mapped address space, but the matched entry has the valid bit off .
Cause Register ExcCode Value:
TLBL: Reference was a load or an instruction fetch
TLBS: Reference was a store
Additional State Saved:
Entry Vector Used:
General exception vector (offset 0x180)
Bus Error Exception — Instruction Fetch or Data Access
一般地原因是Cache尚未初始化的时候访问了Cached的内存空间所致。因此,要注意在系统上电后,Cache初始化之前,只访问Uncached的地址空间,也就是0xA0000000-0xBFFFFFFF
这一段。默认地,上电初始化的入口点0xBFC00000
就位于这一段。(某些MIPS实现中可以通过外部硬线连接修改入口点地址,但为了不引发无法预料的问题,不要将入口点地址修改为Uncached段以外的地址
)
A bus error exception occurs when an instruction or data access makes a bus request (due to a cache miss or an uncacheable reference) and that request terminates in an error. The bus error exception can occur on either an instruction fetch or a data access. Bus error exceptions that occur on an instruction fetch have a higher priority than bus error exceptions that occur on a data access.
Bus errors taken on the requested (critical) word of an instruction fetch or data load are precise. Other bus errors, such as stores or non-critical words of a burst read, can be imprecise. These errors are taken when the EB_RBErr or EB_WBErr signals are asserted and may occur on an instruction that was not the source of the offending bus cycle.
在指令获取或数据加载的请求(关键)字上接收的总线错误是精确的。 其他总线错误,如存储或突发读取的非关键字,可能是不精确的。 这些错误是在断言EB_RBErr或EB_WBErr信号时发生的,可能发生在一个指令上,而该指令不是发生故障的总线周期的来源。
Cause Register ExcCode Value:
IBE: Error on an instruction reference
DBE: Error on a data reference
Additional State Saved: None
Entry Vector Used:
General exception vector (offset 0x180)
详细异常处理流程图
异常的嵌套
在有的情况下,希望在异常或中断中,系统可以继续处理其他的异常或中断。 这需要系统软件处理如下事情:
进入处理程序后,保存Context, EPC, status, cause等寄存器的值到内存栈中,然后设置UM=0, 设置CPU模式为核心态,(异常并不会改status中UM的值,EXL=1, ERL=1,UM=0
,任意一个条件成立都是内核态,为何要设置为内核态呢,因为只有在内核态才能访问cp0的特权资源),然后清除SR[EXL], 从而支持EPC会被更新,从而支持嵌套处理。但是当还没来得及清除SR[EXL],另外一个异常立即就来了怎么办呢(中断不可能来,只可能是执行异常),那么所有寄存器的值都不会更新,直接跳转到异常向量处开始执行
在任何情况下,reset, softreset,NMI,都会无条件响应,并且设置ERL=1,也就是说错误处理可以无条件相应,当然错误处理也是可以嵌套的。
在ERL=1时,禁止任何中断和异常,除了reset, softreset,NMI
*SR[IE]是一个很重要的位来处理嵌套异常。值得注意的,或容易犯错的一点是:
在做恢复上下文时,要避免重入问题。比如,要用eret返回时, 要建立EPC的值。在此之 前,一定要先关闭中断disable interrupt
. 否 则,EPC可能被冲掉。
下面是一段异常中断返回的例子代码:
1 | /* 读取SR的当前值*/ |
代码实例分析
cfe中的异常处理 fsbl部分:
1 | LEAF( do_chip_init ) |
在这里将ERL和EXL都清除掉了,并且使能了中断
#define CP0_STATUS_KSU_MASK _MM_MAKEMASK(2,3)
#define CP0_STATUS_KSU_SHIFT (3)
复位后,处在内核态,并且cfe中一直处在内核态中,没有发生cpu模块的转换
ssbl部分:
1 | void cfe_main(int a,int b) |
将异常向量保存在_exc_vectab这个表中,那么这个表由谁去调用呢?
1 | LEAF( _exc_entry ) |
那么 _exc_entry又是怎么调用的呢?
1 | static void exc_install_ram_vectors(void) |
清掉BEV标志,因此除了reset, soft reset,NMI外,所有其他的中断向量都在RAM中0X8000_0000
开始处
1 |
TLB异常为何要在分EXL处理呢,主要是为了处理更高效,因为EXL=0时,TLB发生异常的频率很高,所以单独搞个专用的TLB异常向量,可以极大的提升系统性能,因为通用异常处理向量要根据异常码分开处理,效率不高
1 | _exc_setup_locore((intptr_t) CPUCFG_CERRHANDLER); //重新设置cache 错误异常处理向量,这个函数 |
这里实际上是没有定义 CPUCFG_CERRHANDLER的,因此在cfe中cache错误将导致直接重启
1 | static void exc_setup_hw_vector(uint32_t vecoffset, |
kernel中的异常处理
处理程序什么时候安装?
1 | traps_init(arch/mips/kernel/traps.c,setup_arch之后start_kernel调用) |
装的什么?
1 | except_vec3_generic(head.S) #(除了TLB refill例外都用这个入口): /* General exception vector R4000 version. */ |
那个异常处理程序表是如何初始化的呢?
在traps_init中,大家会看到set_exception_vector
(i,handler)这样的代码, 填的就是这张表啦.可是,如果你用souce insigh之类的东西去找那个handler,往往就落空了,??怎么没有handle_ri,handle_tlbl…_?不着急,只不过是一个小trick, 还记得x86中断处理的handler代码吗? 它们是用宏生成的:
1 | entry.S |
认真追究下去,这里的一些宏是很重要的,象SAVE_ALL(include/asm/stackframe.h), 异常处理要高效,正确,这里要非常小心.这是因为硬件做的事情实在太少了.