参考资料
ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition
- B1.8 Exception handling
ARM® Cortex™-A Series Programmer’s Guide Version: 4.0
异常介绍
什么是异常
异常是处理器核在执行程序指令的过程中突然遇到了异常的事情,这些事件包括硬件中断、指令执行错误、用户程序请求服务、内存访问异常、取指令异常等,几乎每种处理器都支持特定的异常处理,中断也是异常的一种。
ARM的异常源
FIQ较IRQ快的原因
ARM核异常处理过程
异常向量表
[!NOTE]
ARM核怎么知道异常向量表放在内存哪个地方呢?
- Cortex-A架构版本之前,是由协处理器(Coprocessor 15)
CP15
的C1
寄存器来决定的,且只能存放在0xFFFF0000
和0x00000000
两者中的一个。- 从Cortex-A架构版本开始,由协处理器
CP15
的c12
寄存器来决定,且不限制起始地址
Cortex-A处理器异常向量表基地址指定
https://developer.arm.com/documentation/den0013/d/Exception-Handling/Exception-priorities?lang=en
The first column in Table 11.1 gives the vector offset within the vector table associated with the particular type of exception. This is a table of instructions that the ARM core jumps to when an exception is raised. These instructions are located in a specific place in memory. The default vector base address is
0x00000000
, but most ARM cores permit the vector base address to be moved to0xFFFF0000
(orHIVECS
). All Cortex-A series processors permit this, and it is the default address selected by the Linux kernel. Cores that implement the Security Extensions can additionally set the vector base address, separately for Secure and Non-secure states, using the CP15 Vector Base Address registers.
CP15 c1
https://documentation-service.arm.com/static/602cf701083323480d479d18?token=
CP15 c12
ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition
[!NOTE]
其中高位[31:5]是向量表的基地址,低位[4:0]是异常向量的偏移量(offset)
SVC/SWI异常处理
在操作系统中的运用
案例代码
.global _start
_start:
b reset
b undefined_instruction
b software_interrupt
b prefetch_abort
b data_abort
b not_used
b irq_interrupt
b fiq_interrupt
undefined_instruction:.word undefined_instruction
prefetch_abort:.word prefetch_abort
data_abort:.word data_abort
not_used:.word not_used
irq_interrupt:.word irq_interrupt
fiq_interrupt:.word fiq_interrupt
reset:
mov r0,#1
mov r1,#2
mov r2,#3
swi #8 @will save CPSR to SPSR and set CPSR(T,M), save PC to LR
mov r3,#4
mov r4,#5
software_interrupt:
ldr sp,=0x4000fff0
stmfd sp!,{r0-r12,lr}
ldr r0,[lr,#-4]
mov r1,#0xff
bic r0,r0,r1,lsl #24
ldmfd sp!,{r0-r12,pc}
stop:
b stop
定义异常向量表
这里我们将异常向量表放在代码段起始地址:
_start:
b reset
b undefined_instruction
b software_interrupt
b prefetch_abort
b data_abort
b not_used
b irq_interrupt
b fiq_interrupt
异常向量表的指令顺序和地址偏移需要遵循规范,当发生异常跳转时处理器会按照规范从异常向量表找到对应的指令来执行
程序入口reset
第一条指令跳转到 reset
reset:
mov r0,#1
mov r1,#2
mov r2,#3
swi #8 @will save CPSR to SPSR and set CPSR(T,M), save PC to LR
mov r3,#4
mov r4,#5
其中我们在 swi
软中断指令前后增加了一些寄存器操作来验证一些事情:
- 中断返回后,中断前的寄存器应该保持原样
- 中断返回后,应该接着下一条指令继续执行
中断跳转——ARM核自动做的事情
- 将当前CPSR保存到异常状态下的SPSR中
- 对CPSR进行如下设置
- 进入ARM状态(对应CPSR的T位)
- 设置工作模式为相应的异常模式(M位)
- 禁止中断(设置I位和F位为1)
- 将当前PC的值保存到LR,以便异常处理完后能够跳转回中断发生的地方继续执行
- 将当前PC设置到异常向量表的相应位置(由于我们执行的是
swi
软中断,因此会根据异常向量表规范,将PC指向向量表的基地址偏移0x08
的位置)
[!NOTE]
由于使用的是ARM9E-S的核,因此没有手动指定向量表基地址时,默认为
0x00000000
。
中断跳转——从向量表到中断服务程序
_start:
b reset
b undefined_instruction
b software_interrupt
b prefetch_abort
b data_abort
b not_used
b irq_interrupt
b fiq_interrupt
根据向量表基地址及偏移量 0x08
能够找到我们编写的 b software_interrupt
,接着跳转到对应的中断服务程序:
software_interrupt:
ldr sp,=0x4000fff0
stmfd sp!,{r0-r12,lr}
ldr r0,[lr,#-4]
mov r1,#0xff
bic r0,r0,r1,lsl #24
ldmfd sp!,{r0-r12,pc}^
-
设置SP,将R0-12、LR压栈保护;
-
将LR偏移
-4
的地址中的内容给到R0,由于LR是由ARM核中断跳转时设置的,因此我们回头看看中断跳转的地方: -
接着将
0xff
送入R1,并将R1左移24位后作为掩码将R0中的高8位清零,得到中断号#8
-
ldmfd sp!,{r0-r12,pc}
,准备返回,出栈复原R0-R12,将LR(指向mov r3,#4
)送到PC,中断返回
[!IMPORTANT]
这里需要注意的是,
ldmfd sp!,{r0-r12,pc}^
中的^
,会将SPSR暂存的值写回CPSR
总结
异常产生时,ARM核自动做的事情
异常生成之前,我们需要做的事情
异常返回
[!NOTE]
S
后缀表明在原有指令基础上,同时将SPSR恢复到C