1,七种异常源
地址偏移量 | 异常 | 异常模式 | 描述 |
---|---|---|---|
0x00000000 | 复位 | SVC | 复位电平有效时,产生复位异常,程序跳转到复位处理程序处执行 |
0x00000004 | 未定义指令 | Undefined | 遇到不能处理的指令时,产生未定义指令异常 |
0x00000008 | 软件中断 | SVC | 执行SWI指令产生,用于用户模式下的程序调用特权操作指令(主要在系统调用时使用) |
0x0000000c | 预取指异常 | Abort中止模式 | 处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取中止异常 |
0x00000010 | 数据异常 | Abort中止模式 | 处理器数据访问指令的地址不存在,或该地址不允许当前指令访问时 产生数据中止异常 |
0x00000014 | - | 未使用 | 未使用 |
0x00000018 | IRQ | IRQ | 外部中断请求有效,且CPSR中的I位为0时,产生IRQ异常 |
0x0000001c | FIQ | FIQ | 快速中断请求引脚有效,且CPSR中的F位为0时,产生FIQ异常 |
2,异常优先级
为什么FIQ的响应速度比IRQ快?
3,异常处理过程
3.1,当异常产生时, ARM core会自动完成下面的工作
1、R14_< exception_mode > = return link //确定返回地址,并保存
2、SPSR_< exception_mode > = CPSR
3、修改CPSR相应的位
(1)CPSR[4:0] = exception mode number //切换到相应异常的工作模式
(2)CPSR[5] = 0 ; //进入ARM状态
(3)If ==Reset or Fiq then //只有在复位和FIQ模式下才会关闭FIQ中断
CPSR[6] = 1 ;
CPSR[7] = 1 ; //任何异常模式下都会关闭IRQ中断
4、PC = exception vector address //PC跳到异常向量入口处
3.2,异常处理——我们需要完成的部分
_start:
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction:
.long undefined_instruction
_software_interrupt:
.long software_interrupt
_prefetch_abort:
.long prefetch_abort
_data_abort:
.long data_abort
_not_used:
.long not_used
_irq:
.long irq
_fiq:
.long fiq
进栈保存现场
处理异常
出栈恢复现场
3.2.1,例—软中断
.text @代码段
@代码一开始先建立异常向量表,将异常处理的入口保护起来(nop表示一条空指令)
b reset @0x00 reset
nop @0x04 undefine instruction
b swi_handle @0x08 software interrupt
nop @0x0c prefetch abort
nop @0x10 data abort
nop @0x14 (reserved)
nop @0x18 irq
nop @0x1c fiq
swi_handle:
stmfd sp!,{r0,lr} @进栈保护现场
mov r0,#6
ldmfd sp!,{r0,pc} @出栈恢复现场
reset:
ldr sp,=stack_base @给栈顶指针一个合法的内存地址
mov r0,#3
swi 2 @程序会跳转到异常向量表中,软中断异常的入口处。
@同时会把下一条指令的位置保存到lr中
mov r0,#5
b reset
.data @数据段
buf:
.space 32
stack_base: @堆栈进栈后,栈顶指针会递减,所以要在堆栈的位置之前申请一段空间
.end @程序结束
4,软中断指令
应用程序通过系统调用访问内核,系统调用要通过软中断来实现。不同的系统调用有不同的软中断号
软中断swi指令执行时,下一条指令地址会存放在lr寄存器中,可以通过
sub r0,lr,#4
ldr r0,[r0]
获取swi指令
4.1,例
.text @代码段
@代码一开始先建立异常向量表,将异常处理的入口保护起来(nop表示一条空指令)
b reset @0x00 reset
nop @0x04 undefine instruction
b swi_handle @0x08 software interrupt
nop @0x0c prefetch abort
nop @0x10 data abort
nop @0x14 (reserved)
nop @0x18 irq
nop @0x1c fiq
swi_handle:
stmfd sp!,{r0,lr} @进栈保护现场
@获取软中断号
sub r0,lr,#4 @获取软中断指令的地址
ldr r0,[r0] @将软中断指令从其地址中取出
bic r0,#0xff000000 @只保留软中断号
@软中断号处理
cmp r0,#2
moveq r1,#2
cmp r0,#3
beq swi_3_function
mov r0,#5
ldmfd sp!,{r0,pc}^ @出栈恢复现场。'^',表示内核会自动把spsr恢复到cpsr
swi_3_function:
mov r1,#3
mov pc,lr
swi_3_function_end:
reset:
ldr sp,=stack_base @给栈顶指针一个合法的内存地址
@切换到普通用户工作模式下
mrs r0,cpsr
ldr r1,=0xffffffe0
and r0,r0,r1
orr r0,#0x10
msr cpsr,r0
mov r0,#2
swi 2 @程序会跳转到异常向量表中,软中断异常的入口处。
@同时会把下一条指令的位置保存到lr中
@同时会切换到svc工作模式下
mov r0,#3
swi 3
mov r0,#4
b reset
.data @数据段
buf:
.space 32
stack_base: @堆栈进栈后,栈顶指针会递减,所以要在堆栈的位置之前申请一段空间
.end @程序结束
5,异常向量表的跳转
通过b指令跳转的话会出现如下问题:
若要跳转的地址过大,则b指令会转不下,编译就会出错
所以要进行优化,通过ldr指令来实现
@代码一开始先建立异常向量表,将异常处理的入口保护起来(nop表示一条空指令)
b reset @0x00 reset
nop @0x04 undefine instruction
b swi_handle @0x08 software interrupt
nop @0x0c prefetch abort
nop @0x10 data abort
nop @0x14 (reserved)
nop @0x18 irq
nop @0x1c fiq
swi_handle:
stmfd sp!,{r0,lr} @进栈保护现场
mov r0,#5
ldmfd sp!,{r0,pc}^ @出栈恢复现场。'^',表示内核会自动把spsr恢复到cpsr
@代码一开始先建立异常向量表,将异常处理的入口保护起来(nop表示一条空指令)
b reset @0x00 reset
ldr pc,_undefine_instruction @0x04 undefine instruction
ldr pc,_software_interrupt @0x08 software interrupt
ldr pc,_prefetch_abort @0x0c prefetch abort
ldr pc,_data_abort @0x10 data abort
ldr pc,_not_use @0x14 (reserved)
ldr pc,_irq @0x18 irq
ldr pc,_fiq @0x1c fiq
_undefine_instruction:
.word _undefine_instruction
_software_interrupt:
.word swi_handle
_prefetch_abort:
.word _prefetch_abort
_data_abort:
.word _data_abort
_not_use:
.word _not_use
_irq:
.word _irq
_fiq:
.word _fiq
swi_handle:
stmfd sp!,{r0,lr} @进栈保护现场
mov r0,#5
ldmfd sp!,{r0,pc}^ @出栈恢复现场。'^',表示内核会自动把spsr恢复到cpsr